added setup option to show dynamite and keys in game panel for EM artwork
[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
2255   UpdatePlayfieldElementCount();
2256
2257   // update game panel control values
2258
2259   // used instead of "level_nr" (for network games)
2260   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2261   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2262
2263   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2264   for (i = 0; i < MAX_NUM_KEYS; i++)
2265     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2266   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2267   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2268
2269   if (game.centered_player_nr == -1)
2270   {
2271     for (i = 0; i < MAX_PLAYERS; i++)
2272     {
2273       // only one player in Supaplex game engine
2274       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2275         break;
2276
2277       for (k = 0; k < MAX_NUM_KEYS; k++)
2278       {
2279         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2280         {
2281           if (game_em.ply[i]->keys & (1 << k))
2282             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2283               get_key_element_from_nr(k);
2284         }
2285         else if (stored_player[i].key[k])
2286           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2287             get_key_element_from_nr(k);
2288       }
2289
2290       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2291         getPlayerInventorySize(i);
2292
2293       if (stored_player[i].num_white_keys > 0)
2294         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2295           EL_DC_KEY_WHITE;
2296
2297       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2298         stored_player[i].num_white_keys;
2299     }
2300   }
2301   else
2302   {
2303     int player_nr = game.centered_player_nr;
2304
2305     for (k = 0; k < MAX_NUM_KEYS; k++)
2306     {
2307       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2308       {
2309         if (game_em.ply[player_nr]->keys & (1 << k))
2310           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2311             get_key_element_from_nr(k);
2312       }
2313       else if (stored_player[player_nr].key[k])
2314         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2315           get_key_element_from_nr(k);
2316     }
2317
2318     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2319       getPlayerInventorySize(player_nr);
2320
2321     if (stored_player[player_nr].num_white_keys > 0)
2322       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2323
2324     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2325       stored_player[player_nr].num_white_keys;
2326   }
2327
2328   // try to display as many collected keys as possible in the default game panel
2329   for (i = STD_NUM_KEYS; i < MAX_NUM_KEYS + 1; i++)     // EMC keys + white key
2330   {
2331     int nr = GAME_PANEL_KEY_1 + i;
2332     int emc_key = get_key_element_from_nr(i);
2333     int element = (i < MAX_NUM_KEYS ? emc_key : EL_DC_KEY_WHITE);
2334     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2335     struct TextPosInfo *pos = gpc->pos;
2336
2337     // check if panel position is undefined for a certain EMC key or white key
2338     if (gpc->value != EL_EMPTY && pos->x == -1 && pos->y == -1)
2339     {
2340       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2341
2342       // 1st try: display key at the same position as normal or EM keys
2343       if (game_panel_controls[nr_new].value == EL_EMPTY)
2344       {
2345         game_panel_controls[nr_new].value = element;
2346       }
2347       else
2348       {
2349         // 2nd try: display key at the next free position in the key panel
2350         for (k = 0; k < STD_NUM_KEYS; k++)
2351         {
2352           nr_new = GAME_PANEL_KEY_1 + k;
2353
2354           if (game_panel_controls[nr_new].value == EL_EMPTY)
2355           {
2356             game_panel_controls[nr_new].value = element;
2357
2358             break;
2359           }
2360         }
2361       }
2362     }
2363   }
2364
2365   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2366   {
2367     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2368       get_inventory_element_from_pos(local_player, i);
2369     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2370       get_inventory_element_from_pos(local_player, -i - 1);
2371   }
2372
2373   game_panel_controls[GAME_PANEL_SCORE].value = score;
2374   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2375
2376   game_panel_controls[GAME_PANEL_TIME].value = time;
2377
2378   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2379   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2380   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2381
2382   if (level.time == 0)
2383     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2384   else
2385     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2386
2387   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2388   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2389
2390   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2391
2392   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2393     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2394      EL_EMPTY);
2395   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2396     local_player->shield_normal_time_left;
2397   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2398     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2399      EL_EMPTY);
2400   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2401     local_player->shield_deadly_time_left;
2402
2403   game_panel_controls[GAME_PANEL_EXIT].value =
2404     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2405
2406   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2407     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2408   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2409     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2410      EL_EMC_MAGIC_BALL_SWITCH);
2411
2412   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2413     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2414   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2415     game.light_time_left;
2416
2417   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2418     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2419   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2420     game.timegate_time_left;
2421
2422   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2423     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2424
2425   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2426     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2427   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2428     game.lenses_time_left;
2429
2430   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2431     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2432   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2433     game.magnify_time_left;
2434
2435   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2436     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2437      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2438      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2439      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2440      EL_BALLOON_SWITCH_NONE);
2441
2442   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2443     local_player->dynabomb_count;
2444   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2445     local_player->dynabomb_size;
2446   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2447     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2448
2449   game_panel_controls[GAME_PANEL_PENGUINS].value =
2450     game.friends_still_needed;
2451
2452   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2453     game.sokoban_objects_still_needed;
2454   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2455     game.sokoban_fields_still_needed;
2456
2457   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2458     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2459
2460   for (i = 0; i < NUM_BELTS; i++)
2461   {
2462     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2463       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2464        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2465     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2466       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2467   }
2468
2469   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2470     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2471   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2472     game.magic_wall_time_left;
2473
2474   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2475     local_player->gravity;
2476
2477   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2478     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2479
2480   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2481     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2482       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2483        game.panel.element[i].id : EL_UNDEFINED);
2484
2485   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2486     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2487       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2488        element_info[game.panel.element_count[i].id].element_count : 0);
2489
2490   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2491     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2492       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2493        element_info[game.panel.ce_score[i].id].collect_score : 0);
2494
2495   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2496     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2497       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2498        element_info[game.panel.ce_score_element[i].id].collect_score :
2499        EL_UNDEFINED);
2500
2501   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2502   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2503   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2504
2505   // update game panel control frames
2506
2507   for (i = 0; game_panel_controls[i].nr != -1; i++)
2508   {
2509     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2510
2511     if (gpc->type == TYPE_ELEMENT)
2512     {
2513       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2514       {
2515         int last_anim_random_frame = gfx.anim_random_frame;
2516         int element = gpc->value;
2517         int graphic = el2panelimg(element);
2518
2519         if (gpc->value != gpc->last_value)
2520         {
2521           gpc->gfx_frame = 0;
2522           gpc->gfx_random = INIT_GFX_RANDOM();
2523         }
2524         else
2525         {
2526           gpc->gfx_frame++;
2527
2528           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2529               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2530             gpc->gfx_random = INIT_GFX_RANDOM();
2531         }
2532
2533         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2534           gfx.anim_random_frame = gpc->gfx_random;
2535
2536         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2537           gpc->gfx_frame = element_info[element].collect_score;
2538
2539         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2540                                               gpc->gfx_frame);
2541
2542         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2543           gfx.anim_random_frame = last_anim_random_frame;
2544       }
2545     }
2546     else if (gpc->type == TYPE_GRAPHIC)
2547     {
2548       if (gpc->graphic != IMG_UNDEFINED)
2549       {
2550         int last_anim_random_frame = gfx.anim_random_frame;
2551         int graphic = gpc->graphic;
2552
2553         if (gpc->value != gpc->last_value)
2554         {
2555           gpc->gfx_frame = 0;
2556           gpc->gfx_random = INIT_GFX_RANDOM();
2557         }
2558         else
2559         {
2560           gpc->gfx_frame++;
2561
2562           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2563               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2564             gpc->gfx_random = INIT_GFX_RANDOM();
2565         }
2566
2567         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2568           gfx.anim_random_frame = gpc->gfx_random;
2569
2570         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2571
2572         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2573           gfx.anim_random_frame = last_anim_random_frame;
2574       }
2575     }
2576   }
2577 }
2578
2579 static void DisplayGameControlValues(void)
2580 {
2581   boolean redraw_panel = FALSE;
2582   int i;
2583
2584   for (i = 0; game_panel_controls[i].nr != -1; i++)
2585   {
2586     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2587
2588     if (PANEL_DEACTIVATED(gpc->pos))
2589       continue;
2590
2591     if (gpc->value == gpc->last_value &&
2592         gpc->frame == gpc->last_frame)
2593       continue;
2594
2595     redraw_panel = TRUE;
2596   }
2597
2598   if (!redraw_panel)
2599     return;
2600
2601   // copy default game door content to main double buffer
2602
2603   // !!! CHECK AGAIN !!!
2604   SetPanelBackground();
2605   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2606   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2607
2608   // redraw game control buttons
2609   RedrawGameButtons();
2610
2611   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2612
2613   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2614   {
2615     int nr = game_panel_order[i].nr;
2616     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2617     struct TextPosInfo *pos = gpc->pos;
2618     int type = gpc->type;
2619     int value = gpc->value;
2620     int frame = gpc->frame;
2621     int size = pos->size;
2622     int font = pos->font;
2623     boolean draw_masked = pos->draw_masked;
2624     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2625
2626     if (PANEL_DEACTIVATED(pos))
2627       continue;
2628
2629     if (pos->class == get_hash_from_key("extra_panel_items") &&
2630         !setup.prefer_extra_panel_items)
2631       continue;
2632
2633     gpc->last_value = value;
2634     gpc->last_frame = frame;
2635
2636     if (type == TYPE_INTEGER)
2637     {
2638       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2639           nr == GAME_PANEL_TIME)
2640       {
2641         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2642
2643         if (use_dynamic_size)           // use dynamic number of digits
2644         {
2645           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2646           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2647           int size2 = size1 + 1;
2648           int font1 = pos->font;
2649           int font2 = pos->font_alt;
2650
2651           size = (value < value_change ? size1 : size2);
2652           font = (value < value_change ? font1 : font2);
2653         }
2654       }
2655
2656       // correct text size if "digits" is zero or less
2657       if (size <= 0)
2658         size = strlen(int2str(value, size));
2659
2660       // dynamically correct text alignment
2661       pos->width = size * getFontWidth(font);
2662
2663       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2664                   int2str(value, size), font, mask_mode);
2665     }
2666     else if (type == TYPE_ELEMENT)
2667     {
2668       int element, graphic;
2669       Bitmap *src_bitmap;
2670       int src_x, src_y;
2671       int width, height;
2672       int dst_x = PANEL_XPOS(pos);
2673       int dst_y = PANEL_YPOS(pos);
2674
2675       if (value != EL_UNDEFINED && value != EL_EMPTY)
2676       {
2677         element = value;
2678         graphic = el2panelimg(value);
2679
2680 #if 0
2681         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2682               element, EL_NAME(element), size);
2683 #endif
2684
2685         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2686           size = TILESIZE;
2687
2688         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2689                               &src_x, &src_y);
2690
2691         width  = graphic_info[graphic].width  * size / TILESIZE;
2692         height = graphic_info[graphic].height * size / TILESIZE;
2693
2694         if (draw_masked)
2695           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2696                            dst_x, dst_y);
2697         else
2698           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2699                      dst_x, dst_y);
2700       }
2701     }
2702     else if (type == TYPE_GRAPHIC)
2703     {
2704       int graphic        = gpc->graphic;
2705       int graphic_active = gpc->graphic_active;
2706       Bitmap *src_bitmap;
2707       int src_x, src_y;
2708       int width, height;
2709       int dst_x = PANEL_XPOS(pos);
2710       int dst_y = PANEL_YPOS(pos);
2711       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2712                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2713
2714       if (graphic != IMG_UNDEFINED && !skip)
2715       {
2716         if (pos->style == STYLE_REVERSE)
2717           value = 100 - value;
2718
2719         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2720
2721         if (pos->direction & MV_HORIZONTAL)
2722         {
2723           width  = graphic_info[graphic_active].width * value / 100;
2724           height = graphic_info[graphic_active].height;
2725
2726           if (pos->direction == MV_LEFT)
2727           {
2728             src_x += graphic_info[graphic_active].width - width;
2729             dst_x += graphic_info[graphic_active].width - width;
2730           }
2731         }
2732         else
2733         {
2734           width  = graphic_info[graphic_active].width;
2735           height = graphic_info[graphic_active].height * value / 100;
2736
2737           if (pos->direction == MV_UP)
2738           {
2739             src_y += graphic_info[graphic_active].height - height;
2740             dst_y += graphic_info[graphic_active].height - height;
2741           }
2742         }
2743
2744         if (draw_masked)
2745           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2746                            dst_x, dst_y);
2747         else
2748           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2749                      dst_x, dst_y);
2750
2751         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2752
2753         if (pos->direction & MV_HORIZONTAL)
2754         {
2755           if (pos->direction == MV_RIGHT)
2756           {
2757             src_x += width;
2758             dst_x += width;
2759           }
2760           else
2761           {
2762             dst_x = PANEL_XPOS(pos);
2763           }
2764
2765           width = graphic_info[graphic].width - width;
2766         }
2767         else
2768         {
2769           if (pos->direction == MV_DOWN)
2770           {
2771             src_y += height;
2772             dst_y += height;
2773           }
2774           else
2775           {
2776             dst_y = PANEL_YPOS(pos);
2777           }
2778
2779           height = graphic_info[graphic].height - height;
2780         }
2781
2782         if (draw_masked)
2783           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2784                            dst_x, dst_y);
2785         else
2786           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2787                      dst_x, dst_y);
2788       }
2789     }
2790     else if (type == TYPE_STRING)
2791     {
2792       boolean active = (value != 0);
2793       char *state_normal = "off";
2794       char *state_active = "on";
2795       char *state = (active ? state_active : state_normal);
2796       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2797                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2798                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2799                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2800
2801       if (nr == GAME_PANEL_GRAVITY_STATE)
2802       {
2803         int font1 = pos->font;          // (used for normal state)
2804         int font2 = pos->font_alt;      // (used for active state)
2805
2806         font = (active ? font2 : font1);
2807       }
2808
2809       if (s != NULL)
2810       {
2811         char *s_cut;
2812
2813         if (size <= 0)
2814         {
2815           // don't truncate output if "chars" is zero or less
2816           size = strlen(s);
2817
2818           // dynamically correct text alignment
2819           pos->width = size * getFontWidth(font);
2820         }
2821
2822         s_cut = getStringCopyN(s, size);
2823
2824         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2825                     s_cut, font, mask_mode);
2826
2827         free(s_cut);
2828       }
2829     }
2830
2831     redraw_mask |= REDRAW_DOOR_1;
2832   }
2833
2834   SetGameStatus(GAME_MODE_PLAYING);
2835 }
2836
2837 void UpdateAndDisplayGameControlValues(void)
2838 {
2839   if (tape.deactivate_display)
2840     return;
2841
2842   UpdateGameControlValues();
2843   DisplayGameControlValues();
2844 }
2845
2846 #if 0
2847 static void UpdateGameDoorValues(void)
2848 {
2849   UpdateGameControlValues();
2850 }
2851 #endif
2852
2853 void DrawGameDoorValues(void)
2854 {
2855   DisplayGameControlValues();
2856 }
2857
2858
2859 // ============================================================================
2860 // InitGameEngine()
2861 // ----------------------------------------------------------------------------
2862 // initialize game engine due to level / tape version number
2863 // ============================================================================
2864
2865 static void InitGameEngine(void)
2866 {
2867   int i, j, k, l, x, y;
2868
2869   // set game engine from tape file when re-playing, else from level file
2870   game.engine_version = (tape.playing ? tape.engine_version :
2871                          level.game_version);
2872
2873   // set single or multi-player game mode (needed for re-playing tapes)
2874   game.team_mode = setup.team_mode;
2875
2876   if (tape.playing)
2877   {
2878     int num_players = 0;
2879
2880     for (i = 0; i < MAX_PLAYERS; i++)
2881       if (tape.player_participates[i])
2882         num_players++;
2883
2884     // multi-player tapes contain input data for more than one player
2885     game.team_mode = (num_players > 1);
2886   }
2887
2888 #if 0
2889   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2890         level.game_version);
2891   Debug("game:init:level", "          tape.file_version   == %06d",
2892         tape.file_version);
2893   Debug("game:init:level", "          tape.game_version   == %06d",
2894         tape.game_version);
2895   Debug("game:init:level", "          tape.engine_version == %06d",
2896         tape.engine_version);
2897   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2898         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2899 #endif
2900
2901   // --------------------------------------------------------------------------
2902   // set flags for bugs and changes according to active game engine version
2903   // --------------------------------------------------------------------------
2904
2905   /*
2906     Summary of bugfix:
2907     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2908
2909     Bug was introduced in version:
2910     2.0.1
2911
2912     Bug was fixed in version:
2913     4.2.0.0
2914
2915     Description:
2916     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2917     but the property "can fall" was missing, which caused some levels to be
2918     unsolvable. This was fixed in version 4.2.0.0.
2919
2920     Affected levels/tapes:
2921     An example for a tape that was fixed by this bugfix is tape 029 from the
2922     level set "rnd_sam_bateman".
2923     The wrong behaviour will still be used for all levels or tapes that were
2924     created/recorded with it. An example for this is tape 023 from the level
2925     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2926   */
2927
2928   boolean use_amoeba_dropping_cannot_fall_bug =
2929     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2930       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2931      (tape.playing &&
2932       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2933       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2934
2935   /*
2936     Summary of bugfix/change:
2937     Fixed move speed of elements entering or leaving magic wall.
2938
2939     Fixed/changed in version:
2940     2.0.1
2941
2942     Description:
2943     Before 2.0.1, move speed of elements entering or leaving magic wall was
2944     twice as fast as it is now.
2945     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2946
2947     Affected levels/tapes:
2948     The first condition is generally needed for all levels/tapes before version
2949     2.0.1, which might use the old behaviour before it was changed; known tapes
2950     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2951     The second condition is an exception from the above case and is needed for
2952     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2953     above, but before it was known that this change would break tapes like the
2954     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2955     although the engine version while recording maybe was before 2.0.1. There
2956     are a lot of tapes that are affected by this exception, like tape 006 from
2957     the level set "rnd_conor_mancone".
2958   */
2959
2960   boolean use_old_move_stepsize_for_magic_wall =
2961     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2962      !(tape.playing &&
2963        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2964        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2965
2966   /*
2967     Summary of bugfix/change:
2968     Fixed handling for custom elements that change when pushed by the player.
2969
2970     Fixed/changed in version:
2971     3.1.0
2972
2973     Description:
2974     Before 3.1.0, custom elements that "change when pushing" changed directly
2975     after the player started pushing them (until then handled in "DigField()").
2976     Since 3.1.0, these custom elements are not changed until the "pushing"
2977     move of the element is finished (now handled in "ContinueMoving()").
2978
2979     Affected levels/tapes:
2980     The first condition is generally needed for all levels/tapes before version
2981     3.1.0, which might use the old behaviour before it was changed; known tapes
2982     that are affected are some tapes from the level set "Walpurgis Gardens" by
2983     Jamie Cullen.
2984     The second condition is an exception from the above case and is needed for
2985     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2986     above (including some development versions of 3.1.0), but before it was
2987     known that this change would break tapes like the above and was fixed in
2988     3.1.1, so that the changed behaviour was active although the engine version
2989     while recording maybe was before 3.1.0. There is at least one tape that is
2990     affected by this exception, which is the tape for the one-level set "Bug
2991     Machine" by Juergen Bonhagen.
2992   */
2993
2994   game.use_change_when_pushing_bug =
2995     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2996      !(tape.playing &&
2997        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2998        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2999
3000   /*
3001     Summary of bugfix/change:
3002     Fixed handling for blocking the field the player leaves when moving.
3003
3004     Fixed/changed in version:
3005     3.1.1
3006
3007     Description:
3008     Before 3.1.1, when "block last field when moving" was enabled, the field
3009     the player is leaving when moving was blocked for the time of the move,
3010     and was directly unblocked afterwards. This resulted in the last field
3011     being blocked for exactly one less than the number of frames of one player
3012     move. Additionally, even when blocking was disabled, the last field was
3013     blocked for exactly one frame.
3014     Since 3.1.1, due to changes in player movement handling, the last field
3015     is not blocked at all when blocking is disabled. When blocking is enabled,
3016     the last field is blocked for exactly the number of frames of one player
3017     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3018     last field is blocked for exactly one more than the number of frames of
3019     one player move.
3020
3021     Affected levels/tapes:
3022     (!!! yet to be determined -- probably many !!!)
3023   */
3024
3025   game.use_block_last_field_bug =
3026     (game.engine_version < VERSION_IDENT(3,1,1,0));
3027
3028   /* various special flags and settings for native Emerald Mine game engine */
3029
3030   game_em.use_single_button =
3031     (game.engine_version > VERSION_IDENT(4,0,0,2));
3032
3033   game_em.use_snap_key_bug =
3034     (game.engine_version < VERSION_IDENT(4,0,1,0));
3035
3036   game_em.use_random_bug =
3037     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3038
3039   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3040
3041   game_em.use_old_explosions            = use_old_em_engine;
3042   game_em.use_old_android               = use_old_em_engine;
3043   game_em.use_old_push_elements         = use_old_em_engine;
3044   game_em.use_old_push_into_acid        = use_old_em_engine;
3045
3046   game_em.use_wrap_around               = !use_old_em_engine;
3047
3048   // --------------------------------------------------------------------------
3049
3050   // set maximal allowed number of custom element changes per game frame
3051   game.max_num_changes_per_frame = 1;
3052
3053   // default scan direction: scan playfield from top/left to bottom/right
3054   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3055
3056   // dynamically adjust element properties according to game engine version
3057   InitElementPropertiesEngine(game.engine_version);
3058
3059   // ---------- initialize special element properties -------------------------
3060
3061   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3062   if (use_amoeba_dropping_cannot_fall_bug)
3063     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3064
3065   // ---------- initialize player's initial move delay ------------------------
3066
3067   // dynamically adjust player properties according to level information
3068   for (i = 0; i < MAX_PLAYERS; i++)
3069     game.initial_move_delay_value[i] =
3070       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3071
3072   // dynamically adjust player properties according to game engine version
3073   for (i = 0; i < MAX_PLAYERS; i++)
3074     game.initial_move_delay[i] =
3075       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3076        game.initial_move_delay_value[i] : 0);
3077
3078   // ---------- initialize player's initial push delay ------------------------
3079
3080   // dynamically adjust player properties according to game engine version
3081   game.initial_push_delay_value =
3082     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3083
3084   // ---------- initialize changing elements ----------------------------------
3085
3086   // initialize changing elements information
3087   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3088   {
3089     struct ElementInfo *ei = &element_info[i];
3090
3091     // this pointer might have been changed in the level editor
3092     ei->change = &ei->change_page[0];
3093
3094     if (!IS_CUSTOM_ELEMENT(i))
3095     {
3096       ei->change->target_element = EL_EMPTY_SPACE;
3097       ei->change->delay_fixed = 0;
3098       ei->change->delay_random = 0;
3099       ei->change->delay_frames = 1;
3100     }
3101
3102     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3103     {
3104       ei->has_change_event[j] = FALSE;
3105
3106       ei->event_page_nr[j] = 0;
3107       ei->event_page[j] = &ei->change_page[0];
3108     }
3109   }
3110
3111   // add changing elements from pre-defined list
3112   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3113   {
3114     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3115     struct ElementInfo *ei = &element_info[ch_delay->element];
3116
3117     ei->change->target_element       = ch_delay->target_element;
3118     ei->change->delay_fixed          = ch_delay->change_delay;
3119
3120     ei->change->pre_change_function  = ch_delay->pre_change_function;
3121     ei->change->change_function      = ch_delay->change_function;
3122     ei->change->post_change_function = ch_delay->post_change_function;
3123
3124     ei->change->can_change = TRUE;
3125     ei->change->can_change_or_has_action = TRUE;
3126
3127     ei->has_change_event[CE_DELAY] = TRUE;
3128
3129     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3130     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3131   }
3132
3133   // ---------- initialize internal run-time variables ------------------------
3134
3135   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3136   {
3137     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3138
3139     for (j = 0; j < ei->num_change_pages; j++)
3140     {
3141       ei->change_page[j].can_change_or_has_action =
3142         (ei->change_page[j].can_change |
3143          ei->change_page[j].has_action);
3144     }
3145   }
3146
3147   // add change events from custom element configuration
3148   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3149   {
3150     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3151
3152     for (j = 0; j < ei->num_change_pages; j++)
3153     {
3154       if (!ei->change_page[j].can_change_or_has_action)
3155         continue;
3156
3157       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3158       {
3159         // only add event page for the first page found with this event
3160         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3161         {
3162           ei->has_change_event[k] = TRUE;
3163
3164           ei->event_page_nr[k] = j;
3165           ei->event_page[k] = &ei->change_page[j];
3166         }
3167       }
3168     }
3169   }
3170
3171   // ---------- initialize reference elements in change conditions ------------
3172
3173   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3174   {
3175     int element = EL_CUSTOM_START + i;
3176     struct ElementInfo *ei = &element_info[element];
3177
3178     for (j = 0; j < ei->num_change_pages; j++)
3179     {
3180       int trigger_element = ei->change_page[j].initial_trigger_element;
3181
3182       if (trigger_element >= EL_PREV_CE_8 &&
3183           trigger_element <= EL_NEXT_CE_8)
3184         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3185
3186       ei->change_page[j].trigger_element = trigger_element;
3187     }
3188   }
3189
3190   // ---------- initialize run-time trigger player and element ----------------
3191
3192   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3193   {
3194     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3195
3196     for (j = 0; j < ei->num_change_pages; j++)
3197     {
3198       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3199       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3200       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3201       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3202       ei->change_page[j].actual_trigger_ce_value = 0;
3203       ei->change_page[j].actual_trigger_ce_score = 0;
3204     }
3205   }
3206
3207   // ---------- initialize trigger events -------------------------------------
3208
3209   // initialize trigger events information
3210   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3211     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3212       trigger_events[i][j] = FALSE;
3213
3214   // add trigger events from element change event properties
3215   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3216   {
3217     struct ElementInfo *ei = &element_info[i];
3218
3219     for (j = 0; j < ei->num_change_pages; j++)
3220     {
3221       if (!ei->change_page[j].can_change_or_has_action)
3222         continue;
3223
3224       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3225       {
3226         int trigger_element = ei->change_page[j].trigger_element;
3227
3228         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3229         {
3230           if (ei->change_page[j].has_event[k])
3231           {
3232             if (IS_GROUP_ELEMENT(trigger_element))
3233             {
3234               struct ElementGroupInfo *group =
3235                 element_info[trigger_element].group;
3236
3237               for (l = 0; l < group->num_elements_resolved; l++)
3238                 trigger_events[group->element_resolved[l]][k] = TRUE;
3239             }
3240             else if (trigger_element == EL_ANY_ELEMENT)
3241               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3242                 trigger_events[l][k] = TRUE;
3243             else
3244               trigger_events[trigger_element][k] = TRUE;
3245           }
3246         }
3247       }
3248     }
3249   }
3250
3251   // ---------- initialize push delay -----------------------------------------
3252
3253   // initialize push delay values to default
3254   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3255   {
3256     if (!IS_CUSTOM_ELEMENT(i))
3257     {
3258       // set default push delay values (corrected since version 3.0.7-1)
3259       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3260       {
3261         element_info[i].push_delay_fixed = 2;
3262         element_info[i].push_delay_random = 8;
3263       }
3264       else
3265       {
3266         element_info[i].push_delay_fixed = 8;
3267         element_info[i].push_delay_random = 8;
3268       }
3269     }
3270   }
3271
3272   // set push delay value for certain elements from pre-defined list
3273   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3274   {
3275     int e = push_delay_list[i].element;
3276
3277     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3278     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3279   }
3280
3281   // set push delay value for Supaplex elements for newer engine versions
3282   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3283   {
3284     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3285     {
3286       if (IS_SP_ELEMENT(i))
3287       {
3288         // set SP push delay to just enough to push under a falling zonk
3289         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3290
3291         element_info[i].push_delay_fixed  = delay;
3292         element_info[i].push_delay_random = 0;
3293       }
3294     }
3295   }
3296
3297   // ---------- initialize move stepsize --------------------------------------
3298
3299   // initialize move stepsize values to default
3300   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3301     if (!IS_CUSTOM_ELEMENT(i))
3302       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3303
3304   // set move stepsize value for certain elements from pre-defined list
3305   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3306   {
3307     int e = move_stepsize_list[i].element;
3308
3309     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3310
3311     // set move stepsize value for certain elements for older engine versions
3312     if (use_old_move_stepsize_for_magic_wall)
3313     {
3314       if (e == EL_MAGIC_WALL_FILLING ||
3315           e == EL_MAGIC_WALL_EMPTYING ||
3316           e == EL_BD_MAGIC_WALL_FILLING ||
3317           e == EL_BD_MAGIC_WALL_EMPTYING)
3318         element_info[e].move_stepsize *= 2;
3319     }
3320   }
3321
3322   // ---------- initialize collect score --------------------------------------
3323
3324   // initialize collect score values for custom elements from initial value
3325   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3326     if (IS_CUSTOM_ELEMENT(i))
3327       element_info[i].collect_score = element_info[i].collect_score_initial;
3328
3329   // ---------- initialize collect count --------------------------------------
3330
3331   // initialize collect count values for non-custom elements
3332   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3333     if (!IS_CUSTOM_ELEMENT(i))
3334       element_info[i].collect_count_initial = 0;
3335
3336   // add collect count values for all elements from pre-defined list
3337   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3338     element_info[collect_count_list[i].element].collect_count_initial =
3339       collect_count_list[i].count;
3340
3341   // ---------- initialize access direction -----------------------------------
3342
3343   // initialize access direction values to default (access from every side)
3344   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3345     if (!IS_CUSTOM_ELEMENT(i))
3346       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3347
3348   // set access direction value for certain elements from pre-defined list
3349   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3350     element_info[access_direction_list[i].element].access_direction =
3351       access_direction_list[i].direction;
3352
3353   // ---------- initialize explosion content ----------------------------------
3354   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3355   {
3356     if (IS_CUSTOM_ELEMENT(i))
3357       continue;
3358
3359     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3360     {
3361       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3362
3363       element_info[i].content.e[x][y] =
3364         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3365          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3366          i == EL_PLAYER_3 ? EL_EMERALD :
3367          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3368          i == EL_MOLE ? EL_EMERALD_RED :
3369          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3370          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3371          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3372          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3373          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3374          i == EL_WALL_EMERALD ? EL_EMERALD :
3375          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3376          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3377          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3378          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3379          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3380          i == EL_WALL_PEARL ? EL_PEARL :
3381          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3382          EL_EMPTY);
3383     }
3384   }
3385
3386   // ---------- initialize recursion detection --------------------------------
3387   recursion_loop_depth = 0;
3388   recursion_loop_detected = FALSE;
3389   recursion_loop_element = EL_UNDEFINED;
3390
3391   // ---------- initialize graphics engine ------------------------------------
3392   game.scroll_delay_value =
3393     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3394      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3395      !setup.forced_scroll_delay           ? 0 :
3396      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3397   game.scroll_delay_value =
3398     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3399
3400   // ---------- initialize game engine snapshots ------------------------------
3401   for (i = 0; i < MAX_PLAYERS; i++)
3402     game.snapshot.last_action[i] = 0;
3403   game.snapshot.changed_action = FALSE;
3404   game.snapshot.collected_item = FALSE;
3405   game.snapshot.mode =
3406     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3407      SNAPSHOT_MODE_EVERY_STEP :
3408      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3409      SNAPSHOT_MODE_EVERY_MOVE :
3410      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3411      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3412   game.snapshot.save_snapshot = FALSE;
3413
3414   // ---------- initialize level time for Supaplex engine ---------------------
3415   // Supaplex levels with time limit currently unsupported -- should be added
3416   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3417     level.time = 0;
3418
3419   // ---------- initialize flags for handling game actions --------------------
3420
3421   // set flags for game actions to default values
3422   game.use_key_actions = TRUE;
3423   game.use_mouse_actions = FALSE;
3424
3425   // when using Mirror Magic game engine, handle mouse events only
3426   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3427   {
3428     game.use_key_actions = FALSE;
3429     game.use_mouse_actions = TRUE;
3430   }
3431
3432   // check for custom elements with mouse click events
3433   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3434   {
3435     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3436     {
3437       int element = EL_CUSTOM_START + i;
3438
3439       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3440           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3441           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3442           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3443         game.use_mouse_actions = TRUE;
3444     }
3445   }
3446 }
3447
3448 static int get_num_special_action(int element, int action_first,
3449                                   int action_last)
3450 {
3451   int num_special_action = 0;
3452   int i, j;
3453
3454   for (i = action_first; i <= action_last; i++)
3455   {
3456     boolean found = FALSE;
3457
3458     for (j = 0; j < NUM_DIRECTIONS; j++)
3459       if (el_act_dir2img(element, i, j) !=
3460           el_act_dir2img(element, ACTION_DEFAULT, j))
3461         found = TRUE;
3462
3463     if (found)
3464       num_special_action++;
3465     else
3466       break;
3467   }
3468
3469   return num_special_action;
3470 }
3471
3472
3473 // ============================================================================
3474 // InitGame()
3475 // ----------------------------------------------------------------------------
3476 // initialize and start new game
3477 // ============================================================================
3478
3479 #if DEBUG_INIT_PLAYER
3480 static void DebugPrintPlayerStatus(char *message)
3481 {
3482   int i;
3483
3484   if (!options.debug)
3485     return;
3486
3487   Debug("game:init:player", "%s:", message);
3488
3489   for (i = 0; i < MAX_PLAYERS; i++)
3490   {
3491     struct PlayerInfo *player = &stored_player[i];
3492
3493     Debug("game:init:player",
3494           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3495           i + 1,
3496           player->present,
3497           player->connected,
3498           player->connected_locally,
3499           player->connected_network,
3500           player->active,
3501           (local_player == player ? " (local player)" : ""));
3502   }
3503 }
3504 #endif
3505
3506 void InitGame(void)
3507 {
3508   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3509   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3510   int fade_mask = REDRAW_FIELD;
3511
3512   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3513   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3514   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3515   int initial_move_dir = MV_DOWN;
3516   int i, j, x, y;
3517
3518   // required here to update video display before fading (FIX THIS)
3519   DrawMaskedBorder(REDRAW_DOOR_2);
3520
3521   if (!game.restart_level)
3522     CloseDoor(DOOR_CLOSE_1);
3523
3524   SetGameStatus(GAME_MODE_PLAYING);
3525
3526   if (level_editor_test_game)
3527     FadeSkipNextFadeOut();
3528   else
3529     FadeSetEnterScreen();
3530
3531   if (CheckFadeAll())
3532     fade_mask = REDRAW_ALL;
3533
3534   FadeLevelSoundsAndMusic();
3535
3536   ExpireSoundLoops(TRUE);
3537
3538   FadeOut(fade_mask);
3539
3540   if (level_editor_test_game)
3541     FadeSkipNextFadeIn();
3542
3543   // needed if different viewport properties defined for playing
3544   ChangeViewportPropertiesIfNeeded();
3545
3546   ClearField();
3547
3548   DrawCompleteVideoDisplay();
3549
3550   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3551
3552   InitGameEngine();
3553   InitGameControlValues();
3554
3555   // initialize tape actions from game when recording tape
3556   if (tape.recording)
3557   {
3558     tape.use_key_actions   = game.use_key_actions;
3559     tape.use_mouse_actions = game.use_mouse_actions;
3560   }
3561
3562   // don't play tapes over network
3563   network_playing = (network.enabled && !tape.playing);
3564
3565   for (i = 0; i < MAX_PLAYERS; i++)
3566   {
3567     struct PlayerInfo *player = &stored_player[i];
3568
3569     player->index_nr = i;
3570     player->index_bit = (1 << i);
3571     player->element_nr = EL_PLAYER_1 + i;
3572
3573     player->present = FALSE;
3574     player->active = FALSE;
3575     player->mapped = FALSE;
3576
3577     player->killed = FALSE;
3578     player->reanimated = FALSE;
3579     player->buried = FALSE;
3580
3581     player->action = 0;
3582     player->effective_action = 0;
3583     player->programmed_action = 0;
3584     player->snap_action = 0;
3585
3586     player->mouse_action.lx = 0;
3587     player->mouse_action.ly = 0;
3588     player->mouse_action.button = 0;
3589     player->mouse_action.button_hint = 0;
3590
3591     player->effective_mouse_action.lx = 0;
3592     player->effective_mouse_action.ly = 0;
3593     player->effective_mouse_action.button = 0;
3594     player->effective_mouse_action.button_hint = 0;
3595
3596     for (j = 0; j < MAX_NUM_KEYS; j++)
3597       player->key[j] = FALSE;
3598
3599     player->num_white_keys = 0;
3600
3601     player->dynabomb_count = 0;
3602     player->dynabomb_size = 1;
3603     player->dynabombs_left = 0;
3604     player->dynabomb_xl = FALSE;
3605
3606     player->MovDir = initial_move_dir;
3607     player->MovPos = 0;
3608     player->GfxPos = 0;
3609     player->GfxDir = initial_move_dir;
3610     player->GfxAction = ACTION_DEFAULT;
3611     player->Frame = 0;
3612     player->StepFrame = 0;
3613
3614     player->initial_element = player->element_nr;
3615     player->artwork_element =
3616       (level.use_artwork_element[i] ? level.artwork_element[i] :
3617        player->element_nr);
3618     player->use_murphy = FALSE;
3619
3620     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3621     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3622
3623     player->gravity = level.initial_player_gravity[i];
3624
3625     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3626
3627     player->actual_frame_counter = 0;
3628
3629     player->step_counter = 0;
3630
3631     player->last_move_dir = initial_move_dir;
3632
3633     player->is_active = FALSE;
3634
3635     player->is_waiting = FALSE;
3636     player->is_moving = FALSE;
3637     player->is_auto_moving = FALSE;
3638     player->is_digging = FALSE;
3639     player->is_snapping = FALSE;
3640     player->is_collecting = FALSE;
3641     player->is_pushing = FALSE;
3642     player->is_switching = FALSE;
3643     player->is_dropping = FALSE;
3644     player->is_dropping_pressed = FALSE;
3645
3646     player->is_bored = FALSE;
3647     player->is_sleeping = FALSE;
3648
3649     player->was_waiting = TRUE;
3650     player->was_moving = FALSE;
3651     player->was_snapping = FALSE;
3652     player->was_dropping = FALSE;
3653
3654     player->force_dropping = FALSE;
3655
3656     player->frame_counter_bored = -1;
3657     player->frame_counter_sleeping = -1;
3658
3659     player->anim_delay_counter = 0;
3660     player->post_delay_counter = 0;
3661
3662     player->dir_waiting = initial_move_dir;
3663     player->action_waiting = ACTION_DEFAULT;
3664     player->last_action_waiting = ACTION_DEFAULT;
3665     player->special_action_bored = ACTION_DEFAULT;
3666     player->special_action_sleeping = ACTION_DEFAULT;
3667
3668     player->switch_x = -1;
3669     player->switch_y = -1;
3670
3671     player->drop_x = -1;
3672     player->drop_y = -1;
3673
3674     player->show_envelope = 0;
3675
3676     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3677
3678     player->push_delay       = -1;      // initialized when pushing starts
3679     player->push_delay_value = game.initial_push_delay_value;
3680
3681     player->drop_delay = 0;
3682     player->drop_pressed_delay = 0;
3683
3684     player->last_jx = -1;
3685     player->last_jy = -1;
3686     player->jx = -1;
3687     player->jy = -1;
3688
3689     player->shield_normal_time_left = 0;
3690     player->shield_deadly_time_left = 0;
3691
3692     player->inventory_infinite_element = EL_UNDEFINED;
3693     player->inventory_size = 0;
3694
3695     if (level.use_initial_inventory[i])
3696     {
3697       for (j = 0; j < level.initial_inventory_size[i]; j++)
3698       {
3699         int element = level.initial_inventory_content[i][j];
3700         int collect_count = element_info[element].collect_count_initial;
3701         int k;
3702
3703         if (!IS_CUSTOM_ELEMENT(element))
3704           collect_count = 1;
3705
3706         if (collect_count == 0)
3707           player->inventory_infinite_element = element;
3708         else
3709           for (k = 0; k < collect_count; k++)
3710             if (player->inventory_size < MAX_INVENTORY_SIZE)
3711               player->inventory_element[player->inventory_size++] = element;
3712       }
3713     }
3714
3715     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3716     SnapField(player, 0, 0);
3717
3718     map_player_action[i] = i;
3719   }
3720
3721   network_player_action_received = FALSE;
3722
3723   // initial null action
3724   if (network_playing)
3725     SendToServer_MovePlayer(MV_NONE);
3726
3727   FrameCounter = 0;
3728   TimeFrames = 0;
3729   TimePlayed = 0;
3730   TimeLeft = level.time;
3731   TapeTime = 0;
3732
3733   ScreenMovDir = MV_NONE;
3734   ScreenMovPos = 0;
3735   ScreenGfxPos = 0;
3736
3737   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3738
3739   game.robot_wheel_x = -1;
3740   game.robot_wheel_y = -1;
3741
3742   game.exit_x = -1;
3743   game.exit_y = -1;
3744
3745   game.all_players_gone = FALSE;
3746
3747   game.LevelSolved = FALSE;
3748   game.GameOver = FALSE;
3749
3750   game.GamePlayed = !tape.playing;
3751
3752   game.LevelSolved_GameWon = FALSE;
3753   game.LevelSolved_GameEnd = FALSE;
3754   game.LevelSolved_SaveTape = FALSE;
3755   game.LevelSolved_SaveScore = FALSE;
3756
3757   game.LevelSolved_CountingTime = 0;
3758   game.LevelSolved_CountingScore = 0;
3759   game.LevelSolved_CountingHealth = 0;
3760
3761   game.panel.active = TRUE;
3762
3763   game.no_time_limit = (level.time == 0);
3764
3765   game.yamyam_content_nr = 0;
3766   game.robot_wheel_active = FALSE;
3767   game.magic_wall_active = FALSE;
3768   game.magic_wall_time_left = 0;
3769   game.light_time_left = 0;
3770   game.timegate_time_left = 0;
3771   game.switchgate_pos = 0;
3772   game.wind_direction = level.wind_direction_initial;
3773
3774   game.score = 0;
3775   game.score_final = 0;
3776
3777   game.health = MAX_HEALTH;
3778   game.health_final = MAX_HEALTH;
3779
3780   game.gems_still_needed = level.gems_needed;
3781   game.sokoban_fields_still_needed = 0;
3782   game.sokoban_objects_still_needed = 0;
3783   game.lights_still_needed = 0;
3784   game.players_still_needed = 0;
3785   game.friends_still_needed = 0;
3786
3787   game.lenses_time_left = 0;
3788   game.magnify_time_left = 0;
3789
3790   game.ball_active = level.ball_active_initial;
3791   game.ball_content_nr = 0;
3792
3793   game.explosions_delayed = TRUE;
3794
3795   game.envelope_active = FALSE;
3796
3797   for (i = 0; i < NUM_BELTS; i++)
3798   {
3799     game.belt_dir[i] = MV_NONE;
3800     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3801   }
3802
3803   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3804     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3805
3806 #if DEBUG_INIT_PLAYER
3807   DebugPrintPlayerStatus("Player status at level initialization");
3808 #endif
3809
3810   SCAN_PLAYFIELD(x, y)
3811   {
3812     Tile[x][y] = Last[x][y] = level.field[x][y];
3813     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3814     ChangeDelay[x][y] = 0;
3815     ChangePage[x][y] = -1;
3816     CustomValue[x][y] = 0;              // initialized in InitField()
3817     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3818     AmoebaNr[x][y] = 0;
3819     WasJustMoving[x][y] = 0;
3820     WasJustFalling[x][y] = 0;
3821     CheckCollision[x][y] = 0;
3822     CheckImpact[x][y] = 0;
3823     Stop[x][y] = FALSE;
3824     Pushed[x][y] = FALSE;
3825
3826     ChangeCount[x][y] = 0;
3827     ChangeEvent[x][y] = -1;
3828
3829     ExplodePhase[x][y] = 0;
3830     ExplodeDelay[x][y] = 0;
3831     ExplodeField[x][y] = EX_TYPE_NONE;
3832
3833     RunnerVisit[x][y] = 0;
3834     PlayerVisit[x][y] = 0;
3835
3836     GfxFrame[x][y] = 0;
3837     GfxRandom[x][y] = INIT_GFX_RANDOM();
3838     GfxElement[x][y] = EL_UNDEFINED;
3839     GfxAction[x][y] = ACTION_DEFAULT;
3840     GfxDir[x][y] = MV_NONE;
3841     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3842   }
3843
3844   SCAN_PLAYFIELD(x, y)
3845   {
3846     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3847       emulate_bd = FALSE;
3848     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3849       emulate_sb = FALSE;
3850     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3851       emulate_sp = FALSE;
3852
3853     InitField(x, y, TRUE);
3854
3855     ResetGfxAnimation(x, y);
3856   }
3857
3858   InitBeltMovement();
3859
3860   for (i = 0; i < MAX_PLAYERS; i++)
3861   {
3862     struct PlayerInfo *player = &stored_player[i];
3863
3864     // set number of special actions for bored and sleeping animation
3865     player->num_special_action_bored =
3866       get_num_special_action(player->artwork_element,
3867                              ACTION_BORING_1, ACTION_BORING_LAST);
3868     player->num_special_action_sleeping =
3869       get_num_special_action(player->artwork_element,
3870                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3871   }
3872
3873   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3874                     emulate_sb ? EMU_SOKOBAN :
3875                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3876
3877   // initialize type of slippery elements
3878   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3879   {
3880     if (!IS_CUSTOM_ELEMENT(i))
3881     {
3882       // default: elements slip down either to the left or right randomly
3883       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3884
3885       // SP style elements prefer to slip down on the left side
3886       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3887         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3888
3889       // BD style elements prefer to slip down on the left side
3890       if (game.emulation == EMU_BOULDERDASH)
3891         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3892     }
3893   }
3894
3895   // initialize explosion and ignition delay
3896   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3897   {
3898     if (!IS_CUSTOM_ELEMENT(i))
3899     {
3900       int num_phase = 8;
3901       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3902                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3903                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3904       int last_phase = (num_phase + 1) * delay;
3905       int half_phase = (num_phase / 2) * delay;
3906
3907       element_info[i].explosion_delay = last_phase - 1;
3908       element_info[i].ignition_delay = half_phase;
3909
3910       if (i == EL_BLACK_ORB)
3911         element_info[i].ignition_delay = 1;
3912     }
3913   }
3914
3915   // correct non-moving belts to start moving left
3916   for (i = 0; i < NUM_BELTS; i++)
3917     if (game.belt_dir[i] == MV_NONE)
3918       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3919
3920 #if USE_NEW_PLAYER_ASSIGNMENTS
3921   // use preferred player also in local single-player mode
3922   if (!network.enabled && !game.team_mode)
3923   {
3924     int new_index_nr = setup.network_player_nr;
3925
3926     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3927     {
3928       for (i = 0; i < MAX_PLAYERS; i++)
3929         stored_player[i].connected_locally = FALSE;
3930
3931       stored_player[new_index_nr].connected_locally = TRUE;
3932     }
3933   }
3934
3935   for (i = 0; i < MAX_PLAYERS; i++)
3936   {
3937     stored_player[i].connected = FALSE;
3938
3939     // in network game mode, the local player might not be the first player
3940     if (stored_player[i].connected_locally)
3941       local_player = &stored_player[i];
3942   }
3943
3944   if (!network.enabled)
3945     local_player->connected = TRUE;
3946
3947   if (tape.playing)
3948   {
3949     for (i = 0; i < MAX_PLAYERS; i++)
3950       stored_player[i].connected = tape.player_participates[i];
3951   }
3952   else if (network.enabled)
3953   {
3954     // add team mode players connected over the network (needed for correct
3955     // assignment of player figures from level to locally playing players)
3956
3957     for (i = 0; i < MAX_PLAYERS; i++)
3958       if (stored_player[i].connected_network)
3959         stored_player[i].connected = TRUE;
3960   }
3961   else if (game.team_mode)
3962   {
3963     // try to guess locally connected team mode players (needed for correct
3964     // assignment of player figures from level to locally playing players)
3965
3966     for (i = 0; i < MAX_PLAYERS; i++)
3967       if (setup.input[i].use_joystick ||
3968           setup.input[i].key.left != KSYM_UNDEFINED)
3969         stored_player[i].connected = TRUE;
3970   }
3971
3972 #if DEBUG_INIT_PLAYER
3973   DebugPrintPlayerStatus("Player status after level initialization");
3974 #endif
3975
3976 #if DEBUG_INIT_PLAYER
3977   Debug("game:init:player", "Reassigning players ...");
3978 #endif
3979
3980   // check if any connected player was not found in playfield
3981   for (i = 0; i < MAX_PLAYERS; i++)
3982   {
3983     struct PlayerInfo *player = &stored_player[i];
3984
3985     if (player->connected && !player->present)
3986     {
3987       struct PlayerInfo *field_player = NULL;
3988
3989 #if DEBUG_INIT_PLAYER
3990       Debug("game:init:player",
3991             "- looking for field player for player %d ...", i + 1);
3992 #endif
3993
3994       // assign first free player found that is present in the playfield
3995
3996       // first try: look for unmapped playfield player that is not connected
3997       for (j = 0; j < MAX_PLAYERS; j++)
3998         if (field_player == NULL &&
3999             stored_player[j].present &&
4000             !stored_player[j].mapped &&
4001             !stored_player[j].connected)
4002           field_player = &stored_player[j];
4003
4004       // second try: look for *any* unmapped playfield player
4005       for (j = 0; j < MAX_PLAYERS; j++)
4006         if (field_player == NULL &&
4007             stored_player[j].present &&
4008             !stored_player[j].mapped)
4009           field_player = &stored_player[j];
4010
4011       if (field_player != NULL)
4012       {
4013         int jx = field_player->jx, jy = field_player->jy;
4014
4015 #if DEBUG_INIT_PLAYER
4016         Debug("game:init:player", "- found player %d",
4017               field_player->index_nr + 1);
4018 #endif
4019
4020         player->present = FALSE;
4021         player->active = FALSE;
4022
4023         field_player->present = TRUE;
4024         field_player->active = TRUE;
4025
4026         /*
4027         player->initial_element = field_player->initial_element;
4028         player->artwork_element = field_player->artwork_element;
4029
4030         player->block_last_field       = field_player->block_last_field;
4031         player->block_delay_adjustment = field_player->block_delay_adjustment;
4032         */
4033
4034         StorePlayer[jx][jy] = field_player->element_nr;
4035
4036         field_player->jx = field_player->last_jx = jx;
4037         field_player->jy = field_player->last_jy = jy;
4038
4039         if (local_player == player)
4040           local_player = field_player;
4041
4042         map_player_action[field_player->index_nr] = i;
4043
4044         field_player->mapped = TRUE;
4045
4046 #if DEBUG_INIT_PLAYER
4047         Debug("game:init:player", "- map_player_action[%d] == %d",
4048               field_player->index_nr + 1, i + 1);
4049 #endif
4050       }
4051     }
4052
4053     if (player->connected && player->present)
4054       player->mapped = TRUE;
4055   }
4056
4057 #if DEBUG_INIT_PLAYER
4058   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4059 #endif
4060
4061 #else
4062
4063   // check if any connected player was not found in playfield
4064   for (i = 0; i < MAX_PLAYERS; i++)
4065   {
4066     struct PlayerInfo *player = &stored_player[i];
4067
4068     if (player->connected && !player->present)
4069     {
4070       for (j = 0; j < MAX_PLAYERS; j++)
4071       {
4072         struct PlayerInfo *field_player = &stored_player[j];
4073         int jx = field_player->jx, jy = field_player->jy;
4074
4075         // assign first free player found that is present in the playfield
4076         if (field_player->present && !field_player->connected)
4077         {
4078           player->present = TRUE;
4079           player->active = TRUE;
4080
4081           field_player->present = FALSE;
4082           field_player->active = FALSE;
4083
4084           player->initial_element = field_player->initial_element;
4085           player->artwork_element = field_player->artwork_element;
4086
4087           player->block_last_field       = field_player->block_last_field;
4088           player->block_delay_adjustment = field_player->block_delay_adjustment;
4089
4090           StorePlayer[jx][jy] = player->element_nr;
4091
4092           player->jx = player->last_jx = jx;
4093           player->jy = player->last_jy = jy;
4094
4095           break;
4096         }
4097       }
4098     }
4099   }
4100 #endif
4101
4102 #if 0
4103   Debug("game:init:player", "local_player->present == %d",
4104         local_player->present);
4105 #endif
4106
4107   // set focus to local player for network games, else to all players
4108   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4109   game.centered_player_nr_next = game.centered_player_nr;
4110   game.set_centered_player = FALSE;
4111   game.set_centered_player_wrap = FALSE;
4112
4113   if (network_playing && tape.recording)
4114   {
4115     // store client dependent player focus when recording network games
4116     tape.centered_player_nr_next = game.centered_player_nr_next;
4117     tape.set_centered_player = TRUE;
4118   }
4119
4120   if (tape.playing)
4121   {
4122     // when playing a tape, eliminate all players who do not participate
4123
4124 #if USE_NEW_PLAYER_ASSIGNMENTS
4125
4126     if (!game.team_mode)
4127     {
4128       for (i = 0; i < MAX_PLAYERS; i++)
4129       {
4130         if (stored_player[i].active &&
4131             !tape.player_participates[map_player_action[i]])
4132         {
4133           struct PlayerInfo *player = &stored_player[i];
4134           int jx = player->jx, jy = player->jy;
4135
4136 #if DEBUG_INIT_PLAYER
4137           Debug("game:init:player", "Removing player %d at (%d, %d)",
4138                 i + 1, jx, jy);
4139 #endif
4140
4141           player->active = FALSE;
4142           StorePlayer[jx][jy] = 0;
4143           Tile[jx][jy] = EL_EMPTY;
4144         }
4145       }
4146     }
4147
4148 #else
4149
4150     for (i = 0; i < MAX_PLAYERS; i++)
4151     {
4152       if (stored_player[i].active &&
4153           !tape.player_participates[i])
4154       {
4155         struct PlayerInfo *player = &stored_player[i];
4156         int jx = player->jx, jy = player->jy;
4157
4158         player->active = FALSE;
4159         StorePlayer[jx][jy] = 0;
4160         Tile[jx][jy] = EL_EMPTY;
4161       }
4162     }
4163 #endif
4164   }
4165   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4166   {
4167     // when in single player mode, eliminate all but the local player
4168
4169     for (i = 0; i < MAX_PLAYERS; i++)
4170     {
4171       struct PlayerInfo *player = &stored_player[i];
4172
4173       if (player->active && player != local_player)
4174       {
4175         int jx = player->jx, jy = player->jy;
4176
4177         player->active = FALSE;
4178         player->present = FALSE;
4179
4180         StorePlayer[jx][jy] = 0;
4181         Tile[jx][jy] = EL_EMPTY;
4182       }
4183     }
4184   }
4185
4186   for (i = 0; i < MAX_PLAYERS; i++)
4187     if (stored_player[i].active)
4188       game.players_still_needed++;
4189
4190   if (level.solved_by_one_player)
4191     game.players_still_needed = 1;
4192
4193   // when recording the game, store which players take part in the game
4194   if (tape.recording)
4195   {
4196 #if USE_NEW_PLAYER_ASSIGNMENTS
4197     for (i = 0; i < MAX_PLAYERS; i++)
4198       if (stored_player[i].connected)
4199         tape.player_participates[i] = TRUE;
4200 #else
4201     for (i = 0; i < MAX_PLAYERS; i++)
4202       if (stored_player[i].active)
4203         tape.player_participates[i] = TRUE;
4204 #endif
4205   }
4206
4207 #if DEBUG_INIT_PLAYER
4208   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4209 #endif
4210
4211   if (BorderElement == EL_EMPTY)
4212   {
4213     SBX_Left = 0;
4214     SBX_Right = lev_fieldx - SCR_FIELDX;
4215     SBY_Upper = 0;
4216     SBY_Lower = lev_fieldy - SCR_FIELDY;
4217   }
4218   else
4219   {
4220     SBX_Left = -1;
4221     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4222     SBY_Upper = -1;
4223     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4224   }
4225
4226   if (full_lev_fieldx <= SCR_FIELDX)
4227     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4228   if (full_lev_fieldy <= SCR_FIELDY)
4229     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4230
4231   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4232     SBX_Left--;
4233   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4234     SBY_Upper--;
4235
4236   // if local player not found, look for custom element that might create
4237   // the player (make some assumptions about the right custom element)
4238   if (!local_player->present)
4239   {
4240     int start_x = 0, start_y = 0;
4241     int found_rating = 0;
4242     int found_element = EL_UNDEFINED;
4243     int player_nr = local_player->index_nr;
4244
4245     SCAN_PLAYFIELD(x, y)
4246     {
4247       int element = Tile[x][y];
4248       int content;
4249       int xx, yy;
4250       boolean is_player;
4251
4252       if (level.use_start_element[player_nr] &&
4253           level.start_element[player_nr] == element &&
4254           found_rating < 4)
4255       {
4256         start_x = x;
4257         start_y = y;
4258
4259         found_rating = 4;
4260         found_element = element;
4261       }
4262
4263       if (!IS_CUSTOM_ELEMENT(element))
4264         continue;
4265
4266       if (CAN_CHANGE(element))
4267       {
4268         for (i = 0; i < element_info[element].num_change_pages; i++)
4269         {
4270           // check for player created from custom element as single target
4271           content = element_info[element].change_page[i].target_element;
4272           is_player = ELEM_IS_PLAYER(content);
4273
4274           if (is_player && (found_rating < 3 ||
4275                             (found_rating == 3 && element < found_element)))
4276           {
4277             start_x = x;
4278             start_y = y;
4279
4280             found_rating = 3;
4281             found_element = element;
4282           }
4283         }
4284       }
4285
4286       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4287       {
4288         // check for player created from custom element as explosion content
4289         content = element_info[element].content.e[xx][yy];
4290         is_player = ELEM_IS_PLAYER(content);
4291
4292         if (is_player && (found_rating < 2 ||
4293                           (found_rating == 2 && element < found_element)))
4294         {
4295           start_x = x + xx - 1;
4296           start_y = y + yy - 1;
4297
4298           found_rating = 2;
4299           found_element = element;
4300         }
4301
4302         if (!CAN_CHANGE(element))
4303           continue;
4304
4305         for (i = 0; i < element_info[element].num_change_pages; i++)
4306         {
4307           // check for player created from custom element as extended target
4308           content =
4309             element_info[element].change_page[i].target_content.e[xx][yy];
4310
4311           is_player = ELEM_IS_PLAYER(content);
4312
4313           if (is_player && (found_rating < 1 ||
4314                             (found_rating == 1 && element < found_element)))
4315           {
4316             start_x = x + xx - 1;
4317             start_y = y + yy - 1;
4318
4319             found_rating = 1;
4320             found_element = element;
4321           }
4322         }
4323       }
4324     }
4325
4326     scroll_x = SCROLL_POSITION_X(start_x);
4327     scroll_y = SCROLL_POSITION_Y(start_y);
4328   }
4329   else
4330   {
4331     scroll_x = SCROLL_POSITION_X(local_player->jx);
4332     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4333   }
4334
4335   // !!! FIX THIS (START) !!!
4336   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4337   {
4338     InitGameEngine_EM();
4339   }
4340   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4341   {
4342     InitGameEngine_SP();
4343   }
4344   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4345   {
4346     InitGameEngine_MM();
4347   }
4348   else
4349   {
4350     DrawLevel(REDRAW_FIELD);
4351     DrawAllPlayers();
4352
4353     // after drawing the level, correct some elements
4354     if (game.timegate_time_left == 0)
4355       CloseAllOpenTimegates();
4356   }
4357
4358   // blit playfield from scroll buffer to normal back buffer for fading in
4359   BlitScreenToBitmap(backbuffer);
4360   // !!! FIX THIS (END) !!!
4361
4362   DrawMaskedBorder(fade_mask);
4363
4364   FadeIn(fade_mask);
4365
4366 #if 1
4367   // full screen redraw is required at this point in the following cases:
4368   // - special editor door undrawn when game was started from level editor
4369   // - drawing area (playfield) was changed and has to be removed completely
4370   redraw_mask = REDRAW_ALL;
4371   BackToFront();
4372 #endif
4373
4374   if (!game.restart_level)
4375   {
4376     // copy default game door content to main double buffer
4377
4378     // !!! CHECK AGAIN !!!
4379     SetPanelBackground();
4380     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4381     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4382   }
4383
4384   SetPanelBackground();
4385   SetDrawBackgroundMask(REDRAW_DOOR_1);
4386
4387   UpdateAndDisplayGameControlValues();
4388
4389   if (!game.restart_level)
4390   {
4391     UnmapGameButtons();
4392     UnmapTapeButtons();
4393
4394     FreeGameButtons();
4395     CreateGameButtons();
4396
4397     MapGameButtons();
4398     MapTapeButtons();
4399
4400     // copy actual game door content to door double buffer for OpenDoor()
4401     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4402
4403     OpenDoor(DOOR_OPEN_ALL);
4404
4405     KeyboardAutoRepeatOffUnlessAutoplay();
4406
4407 #if DEBUG_INIT_PLAYER
4408     DebugPrintPlayerStatus("Player status (final)");
4409 #endif
4410   }
4411
4412   UnmapAllGadgets();
4413
4414   MapGameButtons();
4415   MapTapeButtons();
4416
4417   if (!game.restart_level && !tape.playing)
4418   {
4419     LevelStats_incPlayed(level_nr);
4420
4421     SaveLevelSetup_SeriesInfo();
4422   }
4423
4424   game.restart_level = FALSE;
4425   game.restart_game_message = NULL;
4426   game.request_active = FALSE;
4427
4428   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4429     InitGameActions_MM();
4430
4431   SaveEngineSnapshotToListInitial();
4432
4433   if (!game.restart_level)
4434   {
4435     PlaySound(SND_GAME_STARTING);
4436
4437     if (setup.sound_music)
4438       PlayLevelMusic();
4439   }
4440 }
4441
4442 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4443                         int actual_player_x, int actual_player_y)
4444 {
4445   // this is used for non-R'n'D game engines to update certain engine values
4446
4447   // needed to determine if sounds are played within the visible screen area
4448   scroll_x = actual_scroll_x;
4449   scroll_y = actual_scroll_y;
4450
4451   // needed to get player position for "follow finger" playing input method
4452   local_player->jx = actual_player_x;
4453   local_player->jy = actual_player_y;
4454 }
4455
4456 void InitMovDir(int x, int y)
4457 {
4458   int i, element = Tile[x][y];
4459   static int xy[4][2] =
4460   {
4461     {  0, +1 },
4462     { +1,  0 },
4463     {  0, -1 },
4464     { -1,  0 }
4465   };
4466   static int direction[3][4] =
4467   {
4468     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4469     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4470     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4471   };
4472
4473   switch (element)
4474   {
4475     case EL_BUG_RIGHT:
4476     case EL_BUG_UP:
4477     case EL_BUG_LEFT:
4478     case EL_BUG_DOWN:
4479       Tile[x][y] = EL_BUG;
4480       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4481       break;
4482
4483     case EL_SPACESHIP_RIGHT:
4484     case EL_SPACESHIP_UP:
4485     case EL_SPACESHIP_LEFT:
4486     case EL_SPACESHIP_DOWN:
4487       Tile[x][y] = EL_SPACESHIP;
4488       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4489       break;
4490
4491     case EL_BD_BUTTERFLY_RIGHT:
4492     case EL_BD_BUTTERFLY_UP:
4493     case EL_BD_BUTTERFLY_LEFT:
4494     case EL_BD_BUTTERFLY_DOWN:
4495       Tile[x][y] = EL_BD_BUTTERFLY;
4496       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4497       break;
4498
4499     case EL_BD_FIREFLY_RIGHT:
4500     case EL_BD_FIREFLY_UP:
4501     case EL_BD_FIREFLY_LEFT:
4502     case EL_BD_FIREFLY_DOWN:
4503       Tile[x][y] = EL_BD_FIREFLY;
4504       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4505       break;
4506
4507     case EL_PACMAN_RIGHT:
4508     case EL_PACMAN_UP:
4509     case EL_PACMAN_LEFT:
4510     case EL_PACMAN_DOWN:
4511       Tile[x][y] = EL_PACMAN;
4512       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4513       break;
4514
4515     case EL_YAMYAM_LEFT:
4516     case EL_YAMYAM_RIGHT:
4517     case EL_YAMYAM_UP:
4518     case EL_YAMYAM_DOWN:
4519       Tile[x][y] = EL_YAMYAM;
4520       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4521       break;
4522
4523     case EL_SP_SNIKSNAK:
4524       MovDir[x][y] = MV_UP;
4525       break;
4526
4527     case EL_SP_ELECTRON:
4528       MovDir[x][y] = MV_LEFT;
4529       break;
4530
4531     case EL_MOLE_LEFT:
4532     case EL_MOLE_RIGHT:
4533     case EL_MOLE_UP:
4534     case EL_MOLE_DOWN:
4535       Tile[x][y] = EL_MOLE;
4536       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4537       break;
4538
4539     case EL_SPRING_LEFT:
4540     case EL_SPRING_RIGHT:
4541       Tile[x][y] = EL_SPRING;
4542       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4543       break;
4544
4545     default:
4546       if (IS_CUSTOM_ELEMENT(element))
4547       {
4548         struct ElementInfo *ei = &element_info[element];
4549         int move_direction_initial = ei->move_direction_initial;
4550         int move_pattern = ei->move_pattern;
4551
4552         if (move_direction_initial == MV_START_PREVIOUS)
4553         {
4554           if (MovDir[x][y] != MV_NONE)
4555             return;
4556
4557           move_direction_initial = MV_START_AUTOMATIC;
4558         }
4559
4560         if (move_direction_initial == MV_START_RANDOM)
4561           MovDir[x][y] = 1 << RND(4);
4562         else if (move_direction_initial & MV_ANY_DIRECTION)
4563           MovDir[x][y] = move_direction_initial;
4564         else if (move_pattern == MV_ALL_DIRECTIONS ||
4565                  move_pattern == MV_TURNING_LEFT ||
4566                  move_pattern == MV_TURNING_RIGHT ||
4567                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4568                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4569                  move_pattern == MV_TURNING_RANDOM)
4570           MovDir[x][y] = 1 << RND(4);
4571         else if (move_pattern == MV_HORIZONTAL)
4572           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4573         else if (move_pattern == MV_VERTICAL)
4574           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4575         else if (move_pattern & MV_ANY_DIRECTION)
4576           MovDir[x][y] = element_info[element].move_pattern;
4577         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4578                  move_pattern == MV_ALONG_RIGHT_SIDE)
4579         {
4580           // use random direction as default start direction
4581           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4582             MovDir[x][y] = 1 << RND(4);
4583
4584           for (i = 0; i < NUM_DIRECTIONS; i++)
4585           {
4586             int x1 = x + xy[i][0];
4587             int y1 = y + xy[i][1];
4588
4589             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4590             {
4591               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4592                 MovDir[x][y] = direction[0][i];
4593               else
4594                 MovDir[x][y] = direction[1][i];
4595
4596               break;
4597             }
4598           }
4599         }                
4600       }
4601       else
4602       {
4603         MovDir[x][y] = 1 << RND(4);
4604
4605         if (element != EL_BUG &&
4606             element != EL_SPACESHIP &&
4607             element != EL_BD_BUTTERFLY &&
4608             element != EL_BD_FIREFLY)
4609           break;
4610
4611         for (i = 0; i < NUM_DIRECTIONS; i++)
4612         {
4613           int x1 = x + xy[i][0];
4614           int y1 = y + xy[i][1];
4615
4616           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4617           {
4618             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4619             {
4620               MovDir[x][y] = direction[0][i];
4621               break;
4622             }
4623             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4624                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4625             {
4626               MovDir[x][y] = direction[1][i];
4627               break;
4628             }
4629           }
4630         }
4631       }
4632       break;
4633   }
4634
4635   GfxDir[x][y] = MovDir[x][y];
4636 }
4637
4638 void InitAmoebaNr(int x, int y)
4639 {
4640   int i;
4641   int group_nr = AmoebaNeighbourNr(x, y);
4642
4643   if (group_nr == 0)
4644   {
4645     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4646     {
4647       if (AmoebaCnt[i] == 0)
4648       {
4649         group_nr = i;
4650         break;
4651       }
4652     }
4653   }
4654
4655   AmoebaNr[x][y] = group_nr;
4656   AmoebaCnt[group_nr]++;
4657   AmoebaCnt2[group_nr]++;
4658 }
4659
4660 static void LevelSolved(void)
4661 {
4662   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4663       game.players_still_needed > 0)
4664     return;
4665
4666   game.LevelSolved = TRUE;
4667   game.GameOver = TRUE;
4668
4669   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4670                       game_em.lev->score :
4671                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4672                       game_mm.score :
4673                       game.score);
4674   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4675                        MM_HEALTH(game_mm.laser_overload_value) :
4676                        game.health);
4677
4678   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4679   game.LevelSolved_CountingScore = game.score_final;
4680   game.LevelSolved_CountingHealth = game.health_final;
4681 }
4682
4683 void GameWon(void)
4684 {
4685   static int time_count_steps;
4686   static int time, time_final;
4687   static int score, score_final;
4688   static int health, health_final;
4689   static int game_over_delay_1 = 0;
4690   static int game_over_delay_2 = 0;
4691   static int game_over_delay_3 = 0;
4692   int game_over_delay_value_1 = 50;
4693   int game_over_delay_value_2 = 25;
4694   int game_over_delay_value_3 = 50;
4695
4696   if (!game.LevelSolved_GameWon)
4697   {
4698     int i;
4699
4700     // do not start end game actions before the player stops moving (to exit)
4701     if (local_player->active && local_player->MovPos)
4702       return;
4703
4704     game.LevelSolved_GameWon = TRUE;
4705     game.LevelSolved_SaveTape = tape.recording;
4706     game.LevelSolved_SaveScore = !tape.playing;
4707
4708     if (!tape.playing)
4709     {
4710       LevelStats_incSolved(level_nr);
4711
4712       SaveLevelSetup_SeriesInfo();
4713     }
4714
4715     if (tape.auto_play)         // tape might already be stopped here
4716       tape.auto_play_level_solved = TRUE;
4717
4718     TapeStop();
4719
4720     game_over_delay_1 = 0;
4721     game_over_delay_2 = 0;
4722     game_over_delay_3 = game_over_delay_value_3;
4723
4724     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4725     score = score_final = game.score_final;
4726     health = health_final = game.health_final;
4727
4728     if (level.score[SC_TIME_BONUS] > 0)
4729     {
4730       if (TimeLeft > 0)
4731       {
4732         time_final = 0;
4733         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4734       }
4735       else if (game.no_time_limit && TimePlayed < 999)
4736       {
4737         time_final = 999;
4738         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4739       }
4740
4741       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4742
4743       game_over_delay_1 = game_over_delay_value_1;
4744
4745       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4746       {
4747         health_final = 0;
4748         score_final += health * level.score[SC_TIME_BONUS];
4749
4750         game_over_delay_2 = game_over_delay_value_2;
4751       }
4752
4753       game.score_final = score_final;
4754       game.health_final = health_final;
4755     }
4756
4757     if (level_editor_test_game)
4758     {
4759       time = time_final;
4760       score = score_final;
4761
4762       game.LevelSolved_CountingTime = time;
4763       game.LevelSolved_CountingScore = score;
4764
4765       game_panel_controls[GAME_PANEL_TIME].value = time;
4766       game_panel_controls[GAME_PANEL_SCORE].value = score;
4767
4768       DisplayGameControlValues();
4769     }
4770
4771     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4772     {
4773       // check if last player has left the level
4774       if (game.exit_x >= 0 &&
4775           game.exit_y >= 0)
4776       {
4777         int x = game.exit_x;
4778         int y = game.exit_y;
4779         int element = Tile[x][y];
4780
4781         // close exit door after last player
4782         if ((game.all_players_gone &&
4783              (element == EL_EXIT_OPEN ||
4784               element == EL_SP_EXIT_OPEN ||
4785               element == EL_STEEL_EXIT_OPEN)) ||
4786             element == EL_EM_EXIT_OPEN ||
4787             element == EL_EM_STEEL_EXIT_OPEN)
4788         {
4789
4790           Tile[x][y] =
4791             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4792              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4793              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4794              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4795              EL_EM_STEEL_EXIT_CLOSING);
4796
4797           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4798         }
4799
4800         // player disappears
4801         DrawLevelField(x, y);
4802       }
4803
4804       for (i = 0; i < MAX_PLAYERS; i++)
4805       {
4806         struct PlayerInfo *player = &stored_player[i];
4807
4808         if (player->present)
4809         {
4810           RemovePlayer(player);
4811
4812           // player disappears
4813           DrawLevelField(player->jx, player->jy);
4814         }
4815       }
4816     }
4817
4818     PlaySound(SND_GAME_WINNING);
4819   }
4820
4821   if (game_over_delay_1 > 0)
4822   {
4823     game_over_delay_1--;
4824
4825     return;
4826   }
4827
4828   if (time != time_final)
4829   {
4830     int time_to_go = ABS(time_final - time);
4831     int time_count_dir = (time < time_final ? +1 : -1);
4832
4833     if (time_to_go < time_count_steps)
4834       time_count_steps = 1;
4835
4836     time  += time_count_steps * time_count_dir;
4837     score += time_count_steps * level.score[SC_TIME_BONUS];
4838
4839     game.LevelSolved_CountingTime = time;
4840     game.LevelSolved_CountingScore = score;
4841
4842     game_panel_controls[GAME_PANEL_TIME].value = time;
4843     game_panel_controls[GAME_PANEL_SCORE].value = score;
4844
4845     DisplayGameControlValues();
4846
4847     if (time == time_final)
4848       StopSound(SND_GAME_LEVELTIME_BONUS);
4849     else if (setup.sound_loops)
4850       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4851     else
4852       PlaySound(SND_GAME_LEVELTIME_BONUS);
4853
4854     return;
4855   }
4856
4857   if (game_over_delay_2 > 0)
4858   {
4859     game_over_delay_2--;
4860
4861     return;
4862   }
4863
4864   if (health != health_final)
4865   {
4866     int health_count_dir = (health < health_final ? +1 : -1);
4867
4868     health += health_count_dir;
4869     score  += level.score[SC_TIME_BONUS];
4870
4871     game.LevelSolved_CountingHealth = health;
4872     game.LevelSolved_CountingScore = score;
4873
4874     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4875     game_panel_controls[GAME_PANEL_SCORE].value = score;
4876
4877     DisplayGameControlValues();
4878
4879     if (health == health_final)
4880       StopSound(SND_GAME_LEVELTIME_BONUS);
4881     else if (setup.sound_loops)
4882       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4883     else
4884       PlaySound(SND_GAME_LEVELTIME_BONUS);
4885
4886     return;
4887   }
4888
4889   game.panel.active = FALSE;
4890
4891   if (game_over_delay_3 > 0)
4892   {
4893     game_over_delay_3--;
4894
4895     return;
4896   }
4897
4898   GameEnd();
4899 }
4900
4901 void GameEnd(void)
4902 {
4903   // used instead of "level_nr" (needed for network games)
4904   int last_level_nr = levelset.level_nr;
4905   int hi_pos;
4906
4907   game.LevelSolved_GameEnd = TRUE;
4908
4909   if (game.LevelSolved_SaveTape)
4910   {
4911     // make sure that request dialog to save tape does not open door again
4912     if (!global.use_envelope_request)
4913       CloseDoor(DOOR_CLOSE_1);
4914
4915     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4916   }
4917
4918   // if no tape is to be saved, close both doors simultaneously
4919   CloseDoor(DOOR_CLOSE_ALL);
4920
4921   if (level_editor_test_game)
4922   {
4923     SetGameStatus(GAME_MODE_MAIN);
4924
4925     DrawMainMenu();
4926
4927     return;
4928   }
4929
4930   if (!game.LevelSolved_SaveScore)
4931   {
4932     SetGameStatus(GAME_MODE_MAIN);
4933
4934     DrawMainMenu();
4935
4936     return;
4937   }
4938
4939   if (level_nr == leveldir_current->handicap_level)
4940   {
4941     leveldir_current->handicap_level++;
4942
4943     SaveLevelSetup_SeriesInfo();
4944   }
4945
4946   if (setup.increment_levels &&
4947       level_nr < leveldir_current->last_level &&
4948       !network_playing)
4949   {
4950     level_nr++;         // advance to next level
4951     TapeErase();        // start with empty tape
4952
4953     if (setup.auto_play_next_level)
4954     {
4955       LoadLevel(level_nr);
4956
4957       SaveLevelSetup_SeriesInfo();
4958     }
4959   }
4960
4961   hi_pos = NewHiScore(last_level_nr);
4962
4963   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4964   {
4965     SetGameStatus(GAME_MODE_SCORES);
4966
4967     DrawHallOfFame(last_level_nr, hi_pos);
4968   }
4969   else if (setup.auto_play_next_level && setup.increment_levels &&
4970            last_level_nr < leveldir_current->last_level &&
4971            !network_playing)
4972   {
4973     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4974   }
4975   else
4976   {
4977     SetGameStatus(GAME_MODE_MAIN);
4978
4979     DrawMainMenu();
4980   }
4981 }
4982
4983 int NewHiScore(int level_nr)
4984 {
4985   int k, l;
4986   int position = -1;
4987   boolean one_score_entry_per_name = !program.many_scores_per_name;
4988
4989   LoadScore(level_nr);
4990
4991   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4992       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4993     return -1;
4994
4995   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4996   {
4997     if (game.score_final > highscore[k].Score)
4998     {
4999       // player has made it to the hall of fame
5000
5001       if (k < MAX_SCORE_ENTRIES - 1)
5002       {
5003         int m = MAX_SCORE_ENTRIES - 1;
5004
5005         if (one_score_entry_per_name)
5006         {
5007           for (l = k; l < MAX_SCORE_ENTRIES; l++)
5008             if (strEqual(setup.player_name, highscore[l].Name))
5009               m = l;
5010
5011           if (m == k)   // player's new highscore overwrites his old one
5012             goto put_into_list;
5013         }
5014
5015         for (l = m; l > k; l--)
5016         {
5017           strcpy(highscore[l].Name, highscore[l - 1].Name);
5018           highscore[l].Score = highscore[l - 1].Score;
5019         }
5020       }
5021
5022       put_into_list:
5023
5024       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5025       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5026       highscore[k].Score = game.score_final;
5027       position = k;
5028
5029       break;
5030     }
5031     else if (one_score_entry_per_name &&
5032              !strncmp(setup.player_name, highscore[k].Name,
5033                       MAX_PLAYER_NAME_LEN))
5034       break;    // player already there with a higher score
5035   }
5036
5037   if (position >= 0) 
5038     SaveScore(level_nr);
5039
5040   return position;
5041 }
5042
5043 static int getElementMoveStepsizeExt(int x, int y, int direction)
5044 {
5045   int element = Tile[x][y];
5046   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5047   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5048   int horiz_move = (dx != 0);
5049   int sign = (horiz_move ? dx : dy);
5050   int step = sign * element_info[element].move_stepsize;
5051
5052   // special values for move stepsize for spring and things on conveyor belt
5053   if (horiz_move)
5054   {
5055     if (CAN_FALL(element) &&
5056         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5057       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5058     else if (element == EL_SPRING)
5059       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5060   }
5061
5062   return step;
5063 }
5064
5065 static int getElementMoveStepsize(int x, int y)
5066 {
5067   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5068 }
5069
5070 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5071 {
5072   if (player->GfxAction != action || player->GfxDir != dir)
5073   {
5074     player->GfxAction = action;
5075     player->GfxDir = dir;
5076     player->Frame = 0;
5077     player->StepFrame = 0;
5078   }
5079 }
5080
5081 static void ResetGfxFrame(int x, int y)
5082 {
5083   // profiling showed that "autotest" spends 10~20% of its time in this function
5084   if (DrawingDeactivatedField())
5085     return;
5086
5087   int element = Tile[x][y];
5088   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5089
5090   if (graphic_info[graphic].anim_global_sync)
5091     GfxFrame[x][y] = FrameCounter;
5092   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5093     GfxFrame[x][y] = CustomValue[x][y];
5094   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5095     GfxFrame[x][y] = element_info[element].collect_score;
5096   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5097     GfxFrame[x][y] = ChangeDelay[x][y];
5098 }
5099
5100 static void ResetGfxAnimation(int x, int y)
5101 {
5102   GfxAction[x][y] = ACTION_DEFAULT;
5103   GfxDir[x][y] = MovDir[x][y];
5104   GfxFrame[x][y] = 0;
5105
5106   ResetGfxFrame(x, y);
5107 }
5108
5109 static void ResetRandomAnimationValue(int x, int y)
5110 {
5111   GfxRandom[x][y] = INIT_GFX_RANDOM();
5112 }
5113
5114 static void InitMovingField(int x, int y, int direction)
5115 {
5116   int element = Tile[x][y];
5117   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5118   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5119   int newx = x + dx;
5120   int newy = y + dy;
5121   boolean is_moving_before, is_moving_after;
5122
5123   // check if element was/is moving or being moved before/after mode change
5124   is_moving_before = (WasJustMoving[x][y] != 0);
5125   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5126
5127   // reset animation only for moving elements which change direction of moving
5128   // or which just started or stopped moving
5129   // (else CEs with property "can move" / "not moving" are reset each frame)
5130   if (is_moving_before != is_moving_after ||
5131       direction != MovDir[x][y])
5132     ResetGfxAnimation(x, y);
5133
5134   MovDir[x][y] = direction;
5135   GfxDir[x][y] = direction;
5136
5137   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5138                      direction == MV_DOWN && CAN_FALL(element) ?
5139                      ACTION_FALLING : ACTION_MOVING);
5140
5141   // this is needed for CEs with property "can move" / "not moving"
5142
5143   if (is_moving_after)
5144   {
5145     if (Tile[newx][newy] == EL_EMPTY)
5146       Tile[newx][newy] = EL_BLOCKED;
5147
5148     MovDir[newx][newy] = MovDir[x][y];
5149
5150     CustomValue[newx][newy] = CustomValue[x][y];
5151
5152     GfxFrame[newx][newy] = GfxFrame[x][y];
5153     GfxRandom[newx][newy] = GfxRandom[x][y];
5154     GfxAction[newx][newy] = GfxAction[x][y];
5155     GfxDir[newx][newy] = GfxDir[x][y];
5156   }
5157 }
5158
5159 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5160 {
5161   int direction = MovDir[x][y];
5162   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5163   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5164
5165   *goes_to_x = newx;
5166   *goes_to_y = newy;
5167 }
5168
5169 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5170 {
5171   int oldx = x, oldy = y;
5172   int direction = MovDir[x][y];
5173
5174   if (direction == MV_LEFT)
5175     oldx++;
5176   else if (direction == MV_RIGHT)
5177     oldx--;
5178   else if (direction == MV_UP)
5179     oldy++;
5180   else if (direction == MV_DOWN)
5181     oldy--;
5182
5183   *comes_from_x = oldx;
5184   *comes_from_y = oldy;
5185 }
5186
5187 static int MovingOrBlocked2Element(int x, int y)
5188 {
5189   int element = Tile[x][y];
5190
5191   if (element == EL_BLOCKED)
5192   {
5193     int oldx, oldy;
5194
5195     Blocked2Moving(x, y, &oldx, &oldy);
5196     return Tile[oldx][oldy];
5197   }
5198   else
5199     return element;
5200 }
5201
5202 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5203 {
5204   // like MovingOrBlocked2Element(), but if element is moving
5205   // and (x,y) is the field the moving element is just leaving,
5206   // return EL_BLOCKED instead of the element value
5207   int element = Tile[x][y];
5208
5209   if (IS_MOVING(x, y))
5210   {
5211     if (element == EL_BLOCKED)
5212     {
5213       int oldx, oldy;
5214
5215       Blocked2Moving(x, y, &oldx, &oldy);
5216       return Tile[oldx][oldy];
5217     }
5218     else
5219       return EL_BLOCKED;
5220   }
5221   else
5222     return element;
5223 }
5224
5225 static void RemoveField(int x, int y)
5226 {
5227   Tile[x][y] = EL_EMPTY;
5228
5229   MovPos[x][y] = 0;
5230   MovDir[x][y] = 0;
5231   MovDelay[x][y] = 0;
5232
5233   CustomValue[x][y] = 0;
5234
5235   AmoebaNr[x][y] = 0;
5236   ChangeDelay[x][y] = 0;
5237   ChangePage[x][y] = -1;
5238   Pushed[x][y] = FALSE;
5239
5240   GfxElement[x][y] = EL_UNDEFINED;
5241   GfxAction[x][y] = ACTION_DEFAULT;
5242   GfxDir[x][y] = MV_NONE;
5243 }
5244
5245 static void RemoveMovingField(int x, int y)
5246 {
5247   int oldx = x, oldy = y, newx = x, newy = y;
5248   int element = Tile[x][y];
5249   int next_element = EL_UNDEFINED;
5250
5251   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5252     return;
5253
5254   if (IS_MOVING(x, y))
5255   {
5256     Moving2Blocked(x, y, &newx, &newy);
5257
5258     if (Tile[newx][newy] != EL_BLOCKED)
5259     {
5260       // element is moving, but target field is not free (blocked), but
5261       // already occupied by something different (example: acid pool);
5262       // in this case, only remove the moving field, but not the target
5263
5264       RemoveField(oldx, oldy);
5265
5266       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5267
5268       TEST_DrawLevelField(oldx, oldy);
5269
5270       return;
5271     }
5272   }
5273   else if (element == EL_BLOCKED)
5274   {
5275     Blocked2Moving(x, y, &oldx, &oldy);
5276     if (!IS_MOVING(oldx, oldy))
5277       return;
5278   }
5279
5280   if (element == EL_BLOCKED &&
5281       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5282        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5283        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5284        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5285        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5286        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5287     next_element = get_next_element(Tile[oldx][oldy]);
5288
5289   RemoveField(oldx, oldy);
5290   RemoveField(newx, newy);
5291
5292   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5293
5294   if (next_element != EL_UNDEFINED)
5295     Tile[oldx][oldy] = next_element;
5296
5297   TEST_DrawLevelField(oldx, oldy);
5298   TEST_DrawLevelField(newx, newy);
5299 }
5300
5301 void DrawDynamite(int x, int y)
5302 {
5303   int sx = SCREENX(x), sy = SCREENY(y);
5304   int graphic = el2img(Tile[x][y]);
5305   int frame;
5306
5307   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5308     return;
5309
5310   if (IS_WALKABLE_INSIDE(Back[x][y]))
5311     return;
5312
5313   if (Back[x][y])
5314     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5315   else if (Store[x][y])
5316     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5317
5318   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5319
5320   if (Back[x][y] || Store[x][y])
5321     DrawGraphicThruMask(sx, sy, graphic, frame);
5322   else
5323     DrawGraphic(sx, sy, graphic, frame);
5324 }
5325
5326 static void CheckDynamite(int x, int y)
5327 {
5328   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5329   {
5330     MovDelay[x][y]--;
5331
5332     if (MovDelay[x][y] != 0)
5333     {
5334       DrawDynamite(x, y);
5335       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5336
5337       return;
5338     }
5339   }
5340
5341   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5342
5343   Bang(x, y);
5344 }
5345
5346 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5347 {
5348   boolean num_checked_players = 0;
5349   int i;
5350
5351   for (i = 0; i < MAX_PLAYERS; i++)
5352   {
5353     if (stored_player[i].active)
5354     {
5355       int sx = stored_player[i].jx;
5356       int sy = stored_player[i].jy;
5357
5358       if (num_checked_players == 0)
5359       {
5360         *sx1 = *sx2 = sx;
5361         *sy1 = *sy2 = sy;
5362       }
5363       else
5364       {
5365         *sx1 = MIN(*sx1, sx);
5366         *sy1 = MIN(*sy1, sy);
5367         *sx2 = MAX(*sx2, sx);
5368         *sy2 = MAX(*sy2, sy);
5369       }
5370
5371       num_checked_players++;
5372     }
5373   }
5374 }
5375
5376 static boolean checkIfAllPlayersFitToScreen_RND(void)
5377 {
5378   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5379
5380   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5381
5382   return (sx2 - sx1 < SCR_FIELDX &&
5383           sy2 - sy1 < SCR_FIELDY);
5384 }
5385
5386 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5387 {
5388   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5389
5390   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5391
5392   *sx = (sx1 + sx2) / 2;
5393   *sy = (sy1 + sy2) / 2;
5394 }
5395
5396 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5397                                boolean center_screen, boolean quick_relocation)
5398 {
5399   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5400   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5401   boolean no_delay = (tape.warp_forward);
5402   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5403   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5404   int new_scroll_x, new_scroll_y;
5405
5406   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5407   {
5408     // case 1: quick relocation inside visible screen (without scrolling)
5409
5410     RedrawPlayfield();
5411
5412     return;
5413   }
5414
5415   if (!level.shifted_relocation || center_screen)
5416   {
5417     // relocation _with_ centering of screen
5418
5419     new_scroll_x = SCROLL_POSITION_X(x);
5420     new_scroll_y = SCROLL_POSITION_Y(y);
5421   }
5422   else
5423   {
5424     // relocation _without_ centering of screen
5425
5426     int center_scroll_x = SCROLL_POSITION_X(old_x);
5427     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5428     int offset_x = x + (scroll_x - center_scroll_x);
5429     int offset_y = y + (scroll_y - center_scroll_y);
5430
5431     // for new screen position, apply previous offset to center position
5432     new_scroll_x = SCROLL_POSITION_X(offset_x);
5433     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5434   }
5435
5436   if (quick_relocation)
5437   {
5438     // case 2: quick relocation (redraw without visible scrolling)
5439
5440     scroll_x = new_scroll_x;
5441     scroll_y = new_scroll_y;
5442
5443     RedrawPlayfield();
5444
5445     return;
5446   }
5447
5448   // case 3: visible relocation (with scrolling to new position)
5449
5450   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5451
5452   SetVideoFrameDelay(wait_delay_value);
5453
5454   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5455   {
5456     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5457     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5458
5459     if (dx == 0 && dy == 0)             // no scrolling needed at all
5460       break;
5461
5462     scroll_x -= dx;
5463     scroll_y -= dy;
5464
5465     // set values for horizontal/vertical screen scrolling (half tile size)
5466     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5467     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5468     int pos_x = dx * TILEX / 2;
5469     int pos_y = dy * TILEY / 2;
5470     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5471     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5472
5473     ScrollLevel(dx, dy);
5474     DrawAllPlayers();
5475
5476     // scroll in two steps of half tile size to make things smoother
5477     BlitScreenToBitmapExt_RND(window, fx, fy);
5478
5479     // scroll second step to align at full tile size
5480     BlitScreenToBitmap(window);
5481   }
5482
5483   DrawAllPlayers();
5484   BackToFront();
5485
5486   SetVideoFrameDelay(frame_delay_value_old);
5487 }
5488
5489 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5490 {
5491   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5492   int player_nr = GET_PLAYER_NR(el_player);
5493   struct PlayerInfo *player = &stored_player[player_nr];
5494   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5495   boolean no_delay = (tape.warp_forward);
5496   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5497   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5498   int old_jx = player->jx;
5499   int old_jy = player->jy;
5500   int old_element = Tile[old_jx][old_jy];
5501   int element = Tile[jx][jy];
5502   boolean player_relocated = (old_jx != jx || old_jy != jy);
5503
5504   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5505   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5506   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5507   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5508   int leave_side_horiz = move_dir_horiz;
5509   int leave_side_vert  = move_dir_vert;
5510   int enter_side = enter_side_horiz | enter_side_vert;
5511   int leave_side = leave_side_horiz | leave_side_vert;
5512
5513   if (player->buried)           // do not reanimate dead player
5514     return;
5515
5516   if (!player_relocated)        // no need to relocate the player
5517     return;
5518
5519   if (IS_PLAYER(jx, jy))        // player already placed at new position
5520   {
5521     RemoveField(jx, jy);        // temporarily remove newly placed player
5522     DrawLevelField(jx, jy);
5523   }
5524
5525   if (player->present)
5526   {
5527     while (player->MovPos)
5528     {
5529       ScrollPlayer(player, SCROLL_GO_ON);
5530       ScrollScreen(NULL, SCROLL_GO_ON);
5531
5532       AdvanceFrameAndPlayerCounters(player->index_nr);
5533
5534       DrawPlayer(player);
5535
5536       BackToFront_WithFrameDelay(wait_delay_value);
5537     }
5538
5539     DrawPlayer(player);         // needed here only to cleanup last field
5540     DrawLevelField(player->jx, player->jy);     // remove player graphic
5541
5542     player->is_moving = FALSE;
5543   }
5544
5545   if (IS_CUSTOM_ELEMENT(old_element))
5546     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5547                                CE_LEFT_BY_PLAYER,
5548                                player->index_bit, leave_side);
5549
5550   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5551                                       CE_PLAYER_LEAVES_X,
5552                                       player->index_bit, leave_side);
5553
5554   Tile[jx][jy] = el_player;
5555   InitPlayerField(jx, jy, el_player, TRUE);
5556
5557   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5558      possible that the relocation target field did not contain a player element,
5559      but a walkable element, to which the new player was relocated -- in this
5560      case, restore that (already initialized!) element on the player field */
5561   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5562   {
5563     Tile[jx][jy] = element;     // restore previously existing element
5564   }
5565
5566   // only visually relocate centered player
5567   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5568                      FALSE, level.instant_relocation);
5569
5570   TestIfPlayerTouchesBadThing(jx, jy);
5571   TestIfPlayerTouchesCustomElement(jx, jy);
5572
5573   if (IS_CUSTOM_ELEMENT(element))
5574     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5575                                player->index_bit, enter_side);
5576
5577   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5578                                       player->index_bit, enter_side);
5579
5580   if (player->is_switching)
5581   {
5582     /* ensure that relocation while still switching an element does not cause
5583        a new element to be treated as also switched directly after relocation
5584        (this is important for teleporter switches that teleport the player to
5585        a place where another teleporter switch is in the same direction, which
5586        would then incorrectly be treated as immediately switched before the
5587        direction key that caused the switch was released) */
5588
5589     player->switch_x += jx - old_jx;
5590     player->switch_y += jy - old_jy;
5591   }
5592 }
5593
5594 static void Explode(int ex, int ey, int phase, int mode)
5595 {
5596   int x, y;
5597   int last_phase;
5598   int border_element;
5599
5600   // !!! eliminate this variable !!!
5601   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5602
5603   if (game.explosions_delayed)
5604   {
5605     ExplodeField[ex][ey] = mode;
5606     return;
5607   }
5608
5609   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5610   {
5611     int center_element = Tile[ex][ey];
5612     int artwork_element, explosion_element;     // set these values later
5613
5614     // remove things displayed in background while burning dynamite
5615     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5616       Back[ex][ey] = 0;
5617
5618     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5619     {
5620       // put moving element to center field (and let it explode there)
5621       center_element = MovingOrBlocked2Element(ex, ey);
5622       RemoveMovingField(ex, ey);
5623       Tile[ex][ey] = center_element;
5624     }
5625
5626     // now "center_element" is finally determined -- set related values now
5627     artwork_element = center_element;           // for custom player artwork
5628     explosion_element = center_element;         // for custom player artwork
5629
5630     if (IS_PLAYER(ex, ey))
5631     {
5632       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5633
5634       artwork_element = stored_player[player_nr].artwork_element;
5635
5636       if (level.use_explosion_element[player_nr])
5637       {
5638         explosion_element = level.explosion_element[player_nr];
5639         artwork_element = explosion_element;
5640       }
5641     }
5642
5643     if (mode == EX_TYPE_NORMAL ||
5644         mode == EX_TYPE_CENTER ||
5645         mode == EX_TYPE_CROSS)
5646       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5647
5648     last_phase = element_info[explosion_element].explosion_delay + 1;
5649
5650     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5651     {
5652       int xx = x - ex + 1;
5653       int yy = y - ey + 1;
5654       int element;
5655
5656       if (!IN_LEV_FIELD(x, y) ||
5657           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5658           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5659         continue;
5660
5661       element = Tile[x][y];
5662
5663       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5664       {
5665         element = MovingOrBlocked2Element(x, y);
5666
5667         if (!IS_EXPLOSION_PROOF(element))
5668           RemoveMovingField(x, y);
5669       }
5670
5671       // indestructible elements can only explode in center (but not flames)
5672       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5673                                            mode == EX_TYPE_BORDER)) ||
5674           element == EL_FLAMES)
5675         continue;
5676
5677       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5678          behaviour, for example when touching a yamyam that explodes to rocks
5679          with active deadly shield, a rock is created under the player !!! */
5680       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5681 #if 0
5682       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5683           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5684            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5685 #else
5686       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5687 #endif
5688       {
5689         if (IS_ACTIVE_BOMB(element))
5690         {
5691           // re-activate things under the bomb like gate or penguin
5692           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5693           Back[x][y] = 0;
5694         }
5695
5696         continue;
5697       }
5698
5699       // save walkable background elements while explosion on same tile
5700       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5701           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5702         Back[x][y] = element;
5703
5704       // ignite explodable elements reached by other explosion
5705       if (element == EL_EXPLOSION)
5706         element = Store2[x][y];
5707
5708       if (AmoebaNr[x][y] &&
5709           (element == EL_AMOEBA_FULL ||
5710            element == EL_BD_AMOEBA ||
5711            element == EL_AMOEBA_GROWING))
5712       {
5713         AmoebaCnt[AmoebaNr[x][y]]--;
5714         AmoebaCnt2[AmoebaNr[x][y]]--;
5715       }
5716
5717       RemoveField(x, y);
5718
5719       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5720       {
5721         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5722
5723         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5724
5725         if (PLAYERINFO(ex, ey)->use_murphy)
5726           Store[x][y] = EL_EMPTY;
5727       }
5728
5729       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5730       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5731       else if (ELEM_IS_PLAYER(center_element))
5732         Store[x][y] = EL_EMPTY;
5733       else if (center_element == EL_YAMYAM)
5734         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5735       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5736         Store[x][y] = element_info[center_element].content.e[xx][yy];
5737 #if 1
5738       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5739       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5740       // otherwise) -- FIX THIS !!!
5741       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5742         Store[x][y] = element_info[element].content.e[1][1];
5743 #else
5744       else if (!CAN_EXPLODE(element))
5745         Store[x][y] = element_info[element].content.e[1][1];
5746 #endif
5747       else
5748         Store[x][y] = EL_EMPTY;
5749
5750       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5751           center_element == EL_AMOEBA_TO_DIAMOND)
5752         Store2[x][y] = element;
5753
5754       Tile[x][y] = EL_EXPLOSION;
5755       GfxElement[x][y] = artwork_element;
5756
5757       ExplodePhase[x][y] = 1;
5758       ExplodeDelay[x][y] = last_phase;
5759
5760       Stop[x][y] = TRUE;
5761     }
5762
5763     if (center_element == EL_YAMYAM)
5764       game.yamyam_content_nr =
5765         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5766
5767     return;
5768   }
5769
5770   if (Stop[ex][ey])
5771     return;
5772
5773   x = ex;
5774   y = ey;
5775
5776   if (phase == 1)
5777     GfxFrame[x][y] = 0;         // restart explosion animation
5778
5779   last_phase = ExplodeDelay[x][y];
5780
5781   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5782
5783   // this can happen if the player leaves an explosion just in time
5784   if (GfxElement[x][y] == EL_UNDEFINED)
5785     GfxElement[x][y] = EL_EMPTY;
5786
5787   border_element = Store2[x][y];
5788   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5789     border_element = StorePlayer[x][y];
5790
5791   if (phase == element_info[border_element].ignition_delay ||
5792       phase == last_phase)
5793   {
5794     boolean border_explosion = FALSE;
5795
5796     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5797         !PLAYER_EXPLOSION_PROTECTED(x, y))
5798     {
5799       KillPlayerUnlessExplosionProtected(x, y);
5800       border_explosion = TRUE;
5801     }
5802     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5803     {
5804       Tile[x][y] = Store2[x][y];
5805       Store2[x][y] = 0;
5806       Bang(x, y);
5807       border_explosion = TRUE;
5808     }
5809     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5810     {
5811       AmoebaToDiamond(x, y);
5812       Store2[x][y] = 0;
5813       border_explosion = TRUE;
5814     }
5815
5816     // if an element just explodes due to another explosion (chain-reaction),
5817     // do not immediately end the new explosion when it was the last frame of
5818     // the explosion (as it would be done in the following "if"-statement!)
5819     if (border_explosion && phase == last_phase)
5820       return;
5821   }
5822
5823   if (phase == last_phase)
5824   {
5825     int element;
5826
5827     element = Tile[x][y] = Store[x][y];
5828     Store[x][y] = Store2[x][y] = 0;
5829     GfxElement[x][y] = EL_UNDEFINED;
5830
5831     // player can escape from explosions and might therefore be still alive
5832     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5833         element <= EL_PLAYER_IS_EXPLODING_4)
5834     {
5835       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5836       int explosion_element = EL_PLAYER_1 + player_nr;
5837       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5838       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5839
5840       if (level.use_explosion_element[player_nr])
5841         explosion_element = level.explosion_element[player_nr];
5842
5843       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5844                     element_info[explosion_element].content.e[xx][yy]);
5845     }
5846
5847     // restore probably existing indestructible background element
5848     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5849       element = Tile[x][y] = Back[x][y];
5850     Back[x][y] = 0;
5851
5852     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5853     GfxDir[x][y] = MV_NONE;
5854     ChangeDelay[x][y] = 0;
5855     ChangePage[x][y] = -1;
5856
5857     CustomValue[x][y] = 0;
5858
5859     InitField_WithBug2(x, y, FALSE);
5860
5861     TEST_DrawLevelField(x, y);
5862
5863     TestIfElementTouchesCustomElement(x, y);
5864
5865     if (GFX_CRUMBLED(element))
5866       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5867
5868     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5869       StorePlayer[x][y] = 0;
5870
5871     if (ELEM_IS_PLAYER(element))
5872       RelocatePlayer(x, y, element);
5873   }
5874   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5875   {
5876     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5877     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5878
5879     if (phase == delay)
5880       TEST_DrawLevelFieldCrumbled(x, y);
5881
5882     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5883     {
5884       DrawLevelElement(x, y, Back[x][y]);
5885       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5886     }
5887     else if (IS_WALKABLE_UNDER(Back[x][y]))
5888     {
5889       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5890       DrawLevelElementThruMask(x, y, Back[x][y]);
5891     }
5892     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5893       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5894   }
5895 }
5896
5897 static void DynaExplode(int ex, int ey)
5898 {
5899   int i, j;
5900   int dynabomb_element = Tile[ex][ey];
5901   int dynabomb_size = 1;
5902   boolean dynabomb_xl = FALSE;
5903   struct PlayerInfo *player;
5904   static int xy[4][2] =
5905   {
5906     { 0, -1 },
5907     { -1, 0 },
5908     { +1, 0 },
5909     { 0, +1 }
5910   };
5911
5912   if (IS_ACTIVE_BOMB(dynabomb_element))
5913   {
5914     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5915     dynabomb_size = player->dynabomb_size;
5916     dynabomb_xl = player->dynabomb_xl;
5917     player->dynabombs_left++;
5918   }
5919
5920   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5921
5922   for (i = 0; i < NUM_DIRECTIONS; i++)
5923   {
5924     for (j = 1; j <= dynabomb_size; j++)
5925     {
5926       int x = ex + j * xy[i][0];
5927       int y = ey + j * xy[i][1];
5928       int element;
5929
5930       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
5931         break;
5932
5933       element = Tile[x][y];
5934
5935       // do not restart explosions of fields with active bombs
5936       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5937         continue;
5938
5939       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5940
5941       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5942           !IS_DIGGABLE(element) && !dynabomb_xl)
5943         break;
5944     }
5945   }
5946 }
5947
5948 void Bang(int x, int y)
5949 {
5950   int element = MovingOrBlocked2Element(x, y);
5951   int explosion_type = EX_TYPE_NORMAL;
5952
5953   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5954   {
5955     struct PlayerInfo *player = PLAYERINFO(x, y);
5956
5957     element = Tile[x][y] = player->initial_element;
5958
5959     if (level.use_explosion_element[player->index_nr])
5960     {
5961       int explosion_element = level.explosion_element[player->index_nr];
5962
5963       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5964         explosion_type = EX_TYPE_CROSS;
5965       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5966         explosion_type = EX_TYPE_CENTER;
5967     }
5968   }
5969
5970   switch (element)
5971   {
5972     case EL_BUG:
5973     case EL_SPACESHIP:
5974     case EL_BD_BUTTERFLY:
5975     case EL_BD_FIREFLY:
5976     case EL_YAMYAM:
5977     case EL_DARK_YAMYAM:
5978     case EL_ROBOT:
5979     case EL_PACMAN:
5980     case EL_MOLE:
5981       RaiseScoreElement(element);
5982       break;
5983
5984     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5985     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5986     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5987     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5988     case EL_DYNABOMB_INCREASE_NUMBER:
5989     case EL_DYNABOMB_INCREASE_SIZE:
5990     case EL_DYNABOMB_INCREASE_POWER:
5991       explosion_type = EX_TYPE_DYNA;
5992       break;
5993
5994     case EL_DC_LANDMINE:
5995       explosion_type = EX_TYPE_CENTER;
5996       break;
5997
5998     case EL_PENGUIN:
5999     case EL_LAMP:
6000     case EL_LAMP_ACTIVE:
6001     case EL_AMOEBA_TO_DIAMOND:
6002       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6003         explosion_type = EX_TYPE_CENTER;
6004       break;
6005
6006     default:
6007       if (element_info[element].explosion_type == EXPLODES_CROSS)
6008         explosion_type = EX_TYPE_CROSS;
6009       else if (element_info[element].explosion_type == EXPLODES_1X1)
6010         explosion_type = EX_TYPE_CENTER;
6011       break;
6012   }
6013
6014   if (explosion_type == EX_TYPE_DYNA)
6015     DynaExplode(x, y);
6016   else
6017     Explode(x, y, EX_PHASE_START, explosion_type);
6018
6019   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6020 }
6021
6022 static void SplashAcid(int x, int y)
6023 {
6024   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6025       (!IN_LEV_FIELD(x - 1, y - 2) ||
6026        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6027     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6028
6029   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6030       (!IN_LEV_FIELD(x + 1, y - 2) ||
6031        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6032     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6033
6034   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6035 }
6036
6037 static void InitBeltMovement(void)
6038 {
6039   static int belt_base_element[4] =
6040   {
6041     EL_CONVEYOR_BELT_1_LEFT,
6042     EL_CONVEYOR_BELT_2_LEFT,
6043     EL_CONVEYOR_BELT_3_LEFT,
6044     EL_CONVEYOR_BELT_4_LEFT
6045   };
6046   static int belt_base_active_element[4] =
6047   {
6048     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6049     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6050     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6051     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6052   };
6053
6054   int x, y, i, j;
6055
6056   // set frame order for belt animation graphic according to belt direction
6057   for (i = 0; i < NUM_BELTS; i++)
6058   {
6059     int belt_nr = i;
6060
6061     for (j = 0; j < NUM_BELT_PARTS; j++)
6062     {
6063       int element = belt_base_active_element[belt_nr] + j;
6064       int graphic_1 = el2img(element);
6065       int graphic_2 = el2panelimg(element);
6066
6067       if (game.belt_dir[i] == MV_LEFT)
6068       {
6069         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6070         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6071       }
6072       else
6073       {
6074         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6075         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6076       }
6077     }
6078   }
6079
6080   SCAN_PLAYFIELD(x, y)
6081   {
6082     int element = Tile[x][y];
6083
6084     for (i = 0; i < NUM_BELTS; i++)
6085     {
6086       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6087       {
6088         int e_belt_nr = getBeltNrFromBeltElement(element);
6089         int belt_nr = i;
6090
6091         if (e_belt_nr == belt_nr)
6092         {
6093           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6094
6095           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6096         }
6097       }
6098     }
6099   }
6100 }
6101
6102 static void ToggleBeltSwitch(int x, int y)
6103 {
6104   static int belt_base_element[4] =
6105   {
6106     EL_CONVEYOR_BELT_1_LEFT,
6107     EL_CONVEYOR_BELT_2_LEFT,
6108     EL_CONVEYOR_BELT_3_LEFT,
6109     EL_CONVEYOR_BELT_4_LEFT
6110   };
6111   static int belt_base_active_element[4] =
6112   {
6113     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6114     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6115     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6116     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6117   };
6118   static int belt_base_switch_element[4] =
6119   {
6120     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6121     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6122     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6123     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6124   };
6125   static int belt_move_dir[4] =
6126   {
6127     MV_LEFT,
6128     MV_NONE,
6129     MV_RIGHT,
6130     MV_NONE,
6131   };
6132
6133   int element = Tile[x][y];
6134   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6135   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6136   int belt_dir = belt_move_dir[belt_dir_nr];
6137   int xx, yy, i;
6138
6139   if (!IS_BELT_SWITCH(element))
6140     return;
6141
6142   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6143   game.belt_dir[belt_nr] = belt_dir;
6144
6145   if (belt_dir_nr == 3)
6146     belt_dir_nr = 1;
6147
6148   // set frame order for belt animation graphic according to belt direction
6149   for (i = 0; i < NUM_BELT_PARTS; i++)
6150   {
6151     int element = belt_base_active_element[belt_nr] + i;
6152     int graphic_1 = el2img(element);
6153     int graphic_2 = el2panelimg(element);
6154
6155     if (belt_dir == MV_LEFT)
6156     {
6157       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6158       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6159     }
6160     else
6161     {
6162       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6163       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6164     }
6165   }
6166
6167   SCAN_PLAYFIELD(xx, yy)
6168   {
6169     int element = Tile[xx][yy];
6170
6171     if (IS_BELT_SWITCH(element))
6172     {
6173       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6174
6175       if (e_belt_nr == belt_nr)
6176       {
6177         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6178         TEST_DrawLevelField(xx, yy);
6179       }
6180     }
6181     else if (IS_BELT(element) && belt_dir != MV_NONE)
6182     {
6183       int e_belt_nr = getBeltNrFromBeltElement(element);
6184
6185       if (e_belt_nr == belt_nr)
6186       {
6187         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6188
6189         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6190         TEST_DrawLevelField(xx, yy);
6191       }
6192     }
6193     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6194     {
6195       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6196
6197       if (e_belt_nr == belt_nr)
6198       {
6199         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6200
6201         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6202         TEST_DrawLevelField(xx, yy);
6203       }
6204     }
6205   }
6206 }
6207
6208 static void ToggleSwitchgateSwitch(int x, int y)
6209 {
6210   int xx, yy;
6211
6212   game.switchgate_pos = !game.switchgate_pos;
6213
6214   SCAN_PLAYFIELD(xx, yy)
6215   {
6216     int element = Tile[xx][yy];
6217
6218     if (element == EL_SWITCHGATE_SWITCH_UP)
6219     {
6220       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6221       TEST_DrawLevelField(xx, yy);
6222     }
6223     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6224     {
6225       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6226       TEST_DrawLevelField(xx, yy);
6227     }
6228     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6229     {
6230       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6231       TEST_DrawLevelField(xx, yy);
6232     }
6233     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6234     {
6235       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6236       TEST_DrawLevelField(xx, yy);
6237     }
6238     else if (element == EL_SWITCHGATE_OPEN ||
6239              element == EL_SWITCHGATE_OPENING)
6240     {
6241       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6242
6243       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6244     }
6245     else if (element == EL_SWITCHGATE_CLOSED ||
6246              element == EL_SWITCHGATE_CLOSING)
6247     {
6248       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6249
6250       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6251     }
6252   }
6253 }
6254
6255 static int getInvisibleActiveFromInvisibleElement(int element)
6256 {
6257   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6258           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6259           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6260           element);
6261 }
6262
6263 static int getInvisibleFromInvisibleActiveElement(int element)
6264 {
6265   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6266           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6267           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6268           element);
6269 }
6270
6271 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6272 {
6273   int x, y;
6274
6275   SCAN_PLAYFIELD(x, y)
6276   {
6277     int element = Tile[x][y];
6278
6279     if (element == EL_LIGHT_SWITCH &&
6280         game.light_time_left > 0)
6281     {
6282       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6283       TEST_DrawLevelField(x, y);
6284     }
6285     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6286              game.light_time_left == 0)
6287     {
6288       Tile[x][y] = EL_LIGHT_SWITCH;
6289       TEST_DrawLevelField(x, y);
6290     }
6291     else if (element == EL_EMC_DRIPPER &&
6292              game.light_time_left > 0)
6293     {
6294       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6295       TEST_DrawLevelField(x, y);
6296     }
6297     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6298              game.light_time_left == 0)
6299     {
6300       Tile[x][y] = EL_EMC_DRIPPER;
6301       TEST_DrawLevelField(x, y);
6302     }
6303     else if (element == EL_INVISIBLE_STEELWALL ||
6304              element == EL_INVISIBLE_WALL ||
6305              element == EL_INVISIBLE_SAND)
6306     {
6307       if (game.light_time_left > 0)
6308         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6309
6310       TEST_DrawLevelField(x, y);
6311
6312       // uncrumble neighbour fields, if needed
6313       if (element == EL_INVISIBLE_SAND)
6314         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6315     }
6316     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6317              element == EL_INVISIBLE_WALL_ACTIVE ||
6318              element == EL_INVISIBLE_SAND_ACTIVE)
6319     {
6320       if (game.light_time_left == 0)
6321         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6322
6323       TEST_DrawLevelField(x, y);
6324
6325       // re-crumble neighbour fields, if needed
6326       if (element == EL_INVISIBLE_SAND)
6327         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6328     }
6329   }
6330 }
6331
6332 static void RedrawAllInvisibleElementsForLenses(void)
6333 {
6334   int x, y;
6335
6336   SCAN_PLAYFIELD(x, y)
6337   {
6338     int element = Tile[x][y];
6339
6340     if (element == EL_EMC_DRIPPER &&
6341         game.lenses_time_left > 0)
6342     {
6343       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6344       TEST_DrawLevelField(x, y);
6345     }
6346     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6347              game.lenses_time_left == 0)
6348     {
6349       Tile[x][y] = EL_EMC_DRIPPER;
6350       TEST_DrawLevelField(x, y);
6351     }
6352     else if (element == EL_INVISIBLE_STEELWALL ||
6353              element == EL_INVISIBLE_WALL ||
6354              element == EL_INVISIBLE_SAND)
6355     {
6356       if (game.lenses_time_left > 0)
6357         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6358
6359       TEST_DrawLevelField(x, y);
6360
6361       // uncrumble neighbour fields, if needed
6362       if (element == EL_INVISIBLE_SAND)
6363         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6364     }
6365     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6366              element == EL_INVISIBLE_WALL_ACTIVE ||
6367              element == EL_INVISIBLE_SAND_ACTIVE)
6368     {
6369       if (game.lenses_time_left == 0)
6370         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6371
6372       TEST_DrawLevelField(x, y);
6373
6374       // re-crumble neighbour fields, if needed
6375       if (element == EL_INVISIBLE_SAND)
6376         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6377     }
6378   }
6379 }
6380
6381 static void RedrawAllInvisibleElementsForMagnifier(void)
6382 {
6383   int x, y;
6384
6385   SCAN_PLAYFIELD(x, y)
6386   {
6387     int element = Tile[x][y];
6388
6389     if (element == EL_EMC_FAKE_GRASS &&
6390         game.magnify_time_left > 0)
6391     {
6392       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6393       TEST_DrawLevelField(x, y);
6394     }
6395     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6396              game.magnify_time_left == 0)
6397     {
6398       Tile[x][y] = EL_EMC_FAKE_GRASS;
6399       TEST_DrawLevelField(x, y);
6400     }
6401     else if (IS_GATE_GRAY(element) &&
6402              game.magnify_time_left > 0)
6403     {
6404       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6405                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6406                     IS_EM_GATE_GRAY(element) ?
6407                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6408                     IS_EMC_GATE_GRAY(element) ?
6409                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6410                     IS_DC_GATE_GRAY(element) ?
6411                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6412                     element);
6413       TEST_DrawLevelField(x, y);
6414     }
6415     else if (IS_GATE_GRAY_ACTIVE(element) &&
6416              game.magnify_time_left == 0)
6417     {
6418       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6419                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6420                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6421                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6422                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6423                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6424                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6425                     EL_DC_GATE_WHITE_GRAY :
6426                     element);
6427       TEST_DrawLevelField(x, y);
6428     }
6429   }
6430 }
6431
6432 static void ToggleLightSwitch(int x, int y)
6433 {
6434   int element = Tile[x][y];
6435
6436   game.light_time_left =
6437     (element == EL_LIGHT_SWITCH ?
6438      level.time_light * FRAMES_PER_SECOND : 0);
6439
6440   RedrawAllLightSwitchesAndInvisibleElements();
6441 }
6442
6443 static void ActivateTimegateSwitch(int x, int y)
6444 {
6445   int xx, yy;
6446
6447   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6448
6449   SCAN_PLAYFIELD(xx, yy)
6450   {
6451     int element = Tile[xx][yy];
6452
6453     if (element == EL_TIMEGATE_CLOSED ||
6454         element == EL_TIMEGATE_CLOSING)
6455     {
6456       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6457       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6458     }
6459
6460     /*
6461     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6462     {
6463       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6464       TEST_DrawLevelField(xx, yy);
6465     }
6466     */
6467
6468   }
6469
6470   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6471                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6472 }
6473
6474 static void Impact(int x, int y)
6475 {
6476   boolean last_line = (y == lev_fieldy - 1);
6477   boolean object_hit = FALSE;
6478   boolean impact = (last_line || object_hit);
6479   int element = Tile[x][y];
6480   int smashed = EL_STEELWALL;
6481
6482   if (!last_line)       // check if element below was hit
6483   {
6484     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6485       return;
6486
6487     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6488                                          MovDir[x][y + 1] != MV_DOWN ||
6489                                          MovPos[x][y + 1] <= TILEY / 2));
6490
6491     // do not smash moving elements that left the smashed field in time
6492     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6493         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6494       object_hit = FALSE;
6495
6496 #if USE_QUICKSAND_IMPACT_BUGFIX
6497     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6498     {
6499       RemoveMovingField(x, y + 1);
6500       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6501       Tile[x][y + 2] = EL_ROCK;
6502       TEST_DrawLevelField(x, y + 2);
6503
6504       object_hit = TRUE;
6505     }
6506
6507     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6508     {
6509       RemoveMovingField(x, y + 1);
6510       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6511       Tile[x][y + 2] = EL_ROCK;
6512       TEST_DrawLevelField(x, y + 2);
6513
6514       object_hit = TRUE;
6515     }
6516 #endif
6517
6518     if (object_hit)
6519       smashed = MovingOrBlocked2Element(x, y + 1);
6520
6521     impact = (last_line || object_hit);
6522   }
6523
6524   if (!last_line && smashed == EL_ACID) // element falls into acid
6525   {
6526     SplashAcid(x, y + 1);
6527     return;
6528   }
6529
6530   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6531   // only reset graphic animation if graphic really changes after impact
6532   if (impact &&
6533       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6534   {
6535     ResetGfxAnimation(x, y);
6536     TEST_DrawLevelField(x, y);
6537   }
6538
6539   if (impact && CAN_EXPLODE_IMPACT(element))
6540   {
6541     Bang(x, y);
6542     return;
6543   }
6544   else if (impact && element == EL_PEARL &&
6545            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6546   {
6547     ResetGfxAnimation(x, y);
6548
6549     Tile[x][y] = EL_PEARL_BREAKING;
6550     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6551     return;
6552   }
6553   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6554   {
6555     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6556
6557     return;
6558   }
6559
6560   if (impact && element == EL_AMOEBA_DROP)
6561   {
6562     if (object_hit && IS_PLAYER(x, y + 1))
6563       KillPlayerUnlessEnemyProtected(x, y + 1);
6564     else if (object_hit && smashed == EL_PENGUIN)
6565       Bang(x, y + 1);
6566     else
6567     {
6568       Tile[x][y] = EL_AMOEBA_GROWING;
6569       Store[x][y] = EL_AMOEBA_WET;
6570
6571       ResetRandomAnimationValue(x, y);
6572     }
6573     return;
6574   }
6575
6576   if (object_hit)               // check which object was hit
6577   {
6578     if ((CAN_PASS_MAGIC_WALL(element) && 
6579          (smashed == EL_MAGIC_WALL ||
6580           smashed == EL_BD_MAGIC_WALL)) ||
6581         (CAN_PASS_DC_MAGIC_WALL(element) &&
6582          smashed == EL_DC_MAGIC_WALL))
6583     {
6584       int xx, yy;
6585       int activated_magic_wall =
6586         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6587          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6588          EL_DC_MAGIC_WALL_ACTIVE);
6589
6590       // activate magic wall / mill
6591       SCAN_PLAYFIELD(xx, yy)
6592       {
6593         if (Tile[xx][yy] == smashed)
6594           Tile[xx][yy] = activated_magic_wall;
6595       }
6596
6597       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6598       game.magic_wall_active = TRUE;
6599
6600       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6601                             SND_MAGIC_WALL_ACTIVATING :
6602                             smashed == EL_BD_MAGIC_WALL ?
6603                             SND_BD_MAGIC_WALL_ACTIVATING :
6604                             SND_DC_MAGIC_WALL_ACTIVATING));
6605     }
6606
6607     if (IS_PLAYER(x, y + 1))
6608     {
6609       if (CAN_SMASH_PLAYER(element))
6610       {
6611         KillPlayerUnlessEnemyProtected(x, y + 1);
6612         return;
6613       }
6614     }
6615     else if (smashed == EL_PENGUIN)
6616     {
6617       if (CAN_SMASH_PLAYER(element))
6618       {
6619         Bang(x, y + 1);
6620         return;
6621       }
6622     }
6623     else if (element == EL_BD_DIAMOND)
6624     {
6625       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6626       {
6627         Bang(x, y + 1);
6628         return;
6629       }
6630     }
6631     else if (((element == EL_SP_INFOTRON ||
6632                element == EL_SP_ZONK) &&
6633               (smashed == EL_SP_SNIKSNAK ||
6634                smashed == EL_SP_ELECTRON ||
6635                smashed == EL_SP_DISK_ORANGE)) ||
6636              (element == EL_SP_INFOTRON &&
6637               smashed == EL_SP_DISK_YELLOW))
6638     {
6639       Bang(x, y + 1);
6640       return;
6641     }
6642     else if (CAN_SMASH_EVERYTHING(element))
6643     {
6644       if (IS_CLASSIC_ENEMY(smashed) ||
6645           CAN_EXPLODE_SMASHED(smashed))
6646       {
6647         Bang(x, y + 1);
6648         return;
6649       }
6650       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6651       {
6652         if (smashed == EL_LAMP ||
6653             smashed == EL_LAMP_ACTIVE)
6654         {
6655           Bang(x, y + 1);
6656           return;
6657         }
6658         else if (smashed == EL_NUT)
6659         {
6660           Tile[x][y + 1] = EL_NUT_BREAKING;
6661           PlayLevelSound(x, y, SND_NUT_BREAKING);
6662           RaiseScoreElement(EL_NUT);
6663           return;
6664         }
6665         else if (smashed == EL_PEARL)
6666         {
6667           ResetGfxAnimation(x, y);
6668
6669           Tile[x][y + 1] = EL_PEARL_BREAKING;
6670           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6671           return;
6672         }
6673         else if (smashed == EL_DIAMOND)
6674         {
6675           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6676           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6677           return;
6678         }
6679         else if (IS_BELT_SWITCH(smashed))
6680         {
6681           ToggleBeltSwitch(x, y + 1);
6682         }
6683         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6684                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6685                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6686                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6687         {
6688           ToggleSwitchgateSwitch(x, y + 1);
6689         }
6690         else if (smashed == EL_LIGHT_SWITCH ||
6691                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6692         {
6693           ToggleLightSwitch(x, y + 1);
6694         }
6695         else
6696         {
6697           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6698
6699           CheckElementChangeBySide(x, y + 1, smashed, element,
6700                                    CE_SWITCHED, CH_SIDE_TOP);
6701           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6702                                             CH_SIDE_TOP);
6703         }
6704       }
6705       else
6706       {
6707         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6708       }
6709     }
6710   }
6711
6712   // play sound of magic wall / mill
6713   if (!last_line &&
6714       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6715        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6716        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6717   {
6718     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6719       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6720     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6721       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6722     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6723       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6724
6725     return;
6726   }
6727
6728   // play sound of object that hits the ground
6729   if (last_line || object_hit)
6730     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6731 }
6732
6733 static void TurnRoundExt(int x, int y)
6734 {
6735   static struct
6736   {
6737     int dx, dy;
6738   } move_xy[] =
6739   {
6740     {  0,  0 },
6741     { -1,  0 },
6742     { +1,  0 },
6743     {  0,  0 },
6744     {  0, -1 },
6745     {  0,  0 }, { 0, 0 }, { 0, 0 },
6746     {  0, +1 }
6747   };
6748   static struct
6749   {
6750     int left, right, back;
6751   } turn[] =
6752   {
6753     { 0,        0,              0        },
6754     { MV_DOWN,  MV_UP,          MV_RIGHT },
6755     { MV_UP,    MV_DOWN,        MV_LEFT  },
6756     { 0,        0,              0        },
6757     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6758     { 0,        0,              0        },
6759     { 0,        0,              0        },
6760     { 0,        0,              0        },
6761     { MV_RIGHT, MV_LEFT,        MV_UP    }
6762   };
6763
6764   int element = Tile[x][y];
6765   int move_pattern = element_info[element].move_pattern;
6766
6767   int old_move_dir = MovDir[x][y];
6768   int left_dir  = turn[old_move_dir].left;
6769   int right_dir = turn[old_move_dir].right;
6770   int back_dir  = turn[old_move_dir].back;
6771
6772   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6773   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6774   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6775   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6776
6777   int left_x  = x + left_dx,  left_y  = y + left_dy;
6778   int right_x = x + right_dx, right_y = y + right_dy;
6779   int move_x  = x + move_dx,  move_y  = y + move_dy;
6780
6781   int xx, yy;
6782
6783   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6784   {
6785     TestIfBadThingTouchesOtherBadThing(x, y);
6786
6787     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6788       MovDir[x][y] = right_dir;
6789     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6790       MovDir[x][y] = left_dir;
6791
6792     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6793       MovDelay[x][y] = 9;
6794     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6795       MovDelay[x][y] = 1;
6796   }
6797   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6798   {
6799     TestIfBadThingTouchesOtherBadThing(x, y);
6800
6801     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6802       MovDir[x][y] = left_dir;
6803     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6804       MovDir[x][y] = right_dir;
6805
6806     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6807       MovDelay[x][y] = 9;
6808     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6809       MovDelay[x][y] = 1;
6810   }
6811   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6812   {
6813     TestIfBadThingTouchesOtherBadThing(x, y);
6814
6815     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6816       MovDir[x][y] = left_dir;
6817     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6818       MovDir[x][y] = right_dir;
6819
6820     if (MovDir[x][y] != old_move_dir)
6821       MovDelay[x][y] = 9;
6822   }
6823   else if (element == EL_YAMYAM)
6824   {
6825     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6826     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6827
6828     if (can_turn_left && can_turn_right)
6829       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6830     else if (can_turn_left)
6831       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6832     else if (can_turn_right)
6833       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6834     else
6835       MovDir[x][y] = back_dir;
6836
6837     MovDelay[x][y] = 16 + 16 * RND(3);
6838   }
6839   else if (element == EL_DARK_YAMYAM)
6840   {
6841     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6842                                                          left_x, left_y);
6843     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6844                                                          right_x, right_y);
6845
6846     if (can_turn_left && can_turn_right)
6847       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6848     else if (can_turn_left)
6849       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6850     else if (can_turn_right)
6851       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6852     else
6853       MovDir[x][y] = back_dir;
6854
6855     MovDelay[x][y] = 16 + 16 * RND(3);
6856   }
6857   else if (element == EL_PACMAN)
6858   {
6859     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6860     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6861
6862     if (can_turn_left && can_turn_right)
6863       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6864     else if (can_turn_left)
6865       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6866     else if (can_turn_right)
6867       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6868     else
6869       MovDir[x][y] = back_dir;
6870
6871     MovDelay[x][y] = 6 + RND(40);
6872   }
6873   else if (element == EL_PIG)
6874   {
6875     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6876     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6877     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6878     boolean should_turn_left, should_turn_right, should_move_on;
6879     int rnd_value = 24;
6880     int rnd = RND(rnd_value);
6881
6882     should_turn_left = (can_turn_left &&
6883                         (!can_move_on ||
6884                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6885                                                    y + back_dy + left_dy)));
6886     should_turn_right = (can_turn_right &&
6887                          (!can_move_on ||
6888                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6889                                                     y + back_dy + right_dy)));
6890     should_move_on = (can_move_on &&
6891                       (!can_turn_left ||
6892                        !can_turn_right ||
6893                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6894                                                  y + move_dy + left_dy) ||
6895                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6896                                                  y + move_dy + right_dy)));
6897
6898     if (should_turn_left || should_turn_right || should_move_on)
6899     {
6900       if (should_turn_left && should_turn_right && should_move_on)
6901         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6902                         rnd < 2 * rnd_value / 3 ? right_dir :
6903                         old_move_dir);
6904       else if (should_turn_left && should_turn_right)
6905         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6906       else if (should_turn_left && should_move_on)
6907         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6908       else if (should_turn_right && should_move_on)
6909         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6910       else if (should_turn_left)
6911         MovDir[x][y] = left_dir;
6912       else if (should_turn_right)
6913         MovDir[x][y] = right_dir;
6914       else if (should_move_on)
6915         MovDir[x][y] = old_move_dir;
6916     }
6917     else if (can_move_on && rnd > rnd_value / 8)
6918       MovDir[x][y] = old_move_dir;
6919     else if (can_turn_left && can_turn_right)
6920       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6921     else if (can_turn_left && rnd > rnd_value / 8)
6922       MovDir[x][y] = left_dir;
6923     else if (can_turn_right && rnd > rnd_value/8)
6924       MovDir[x][y] = right_dir;
6925     else
6926       MovDir[x][y] = back_dir;
6927
6928     xx = x + move_xy[MovDir[x][y]].dx;
6929     yy = y + move_xy[MovDir[x][y]].dy;
6930
6931     if (!IN_LEV_FIELD(xx, yy) ||
6932         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
6933       MovDir[x][y] = old_move_dir;
6934
6935     MovDelay[x][y] = 0;
6936   }
6937   else if (element == EL_DRAGON)
6938   {
6939     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6940     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6941     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6942     int rnd_value = 24;
6943     int rnd = RND(rnd_value);
6944
6945     if (can_move_on && rnd > rnd_value / 8)
6946       MovDir[x][y] = old_move_dir;
6947     else if (can_turn_left && can_turn_right)
6948       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6949     else if (can_turn_left && rnd > rnd_value / 8)
6950       MovDir[x][y] = left_dir;
6951     else if (can_turn_right && rnd > rnd_value / 8)
6952       MovDir[x][y] = right_dir;
6953     else
6954       MovDir[x][y] = back_dir;
6955
6956     xx = x + move_xy[MovDir[x][y]].dx;
6957     yy = y + move_xy[MovDir[x][y]].dy;
6958
6959     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6960       MovDir[x][y] = old_move_dir;
6961
6962     MovDelay[x][y] = 0;
6963   }
6964   else if (element == EL_MOLE)
6965   {
6966     boolean can_move_on =
6967       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6968                             IS_AMOEBOID(Tile[move_x][move_y]) ||
6969                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
6970     if (!can_move_on)
6971     {
6972       boolean can_turn_left =
6973         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6974                               IS_AMOEBOID(Tile[left_x][left_y])));
6975
6976       boolean can_turn_right =
6977         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6978                               IS_AMOEBOID(Tile[right_x][right_y])));
6979
6980       if (can_turn_left && can_turn_right)
6981         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6982       else if (can_turn_left)
6983         MovDir[x][y] = left_dir;
6984       else
6985         MovDir[x][y] = right_dir;
6986     }
6987
6988     if (MovDir[x][y] != old_move_dir)
6989       MovDelay[x][y] = 9;
6990   }
6991   else if (element == EL_BALLOON)
6992   {
6993     MovDir[x][y] = game.wind_direction;
6994     MovDelay[x][y] = 0;
6995   }
6996   else if (element == EL_SPRING)
6997   {
6998     if (MovDir[x][y] & MV_HORIZONTAL)
6999     {
7000       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7001           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7002       {
7003         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7004         ResetGfxAnimation(move_x, move_y);
7005         TEST_DrawLevelField(move_x, move_y);
7006
7007         MovDir[x][y] = back_dir;
7008       }
7009       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7010                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7011         MovDir[x][y] = MV_NONE;
7012     }
7013
7014     MovDelay[x][y] = 0;
7015   }
7016   else if (element == EL_ROBOT ||
7017            element == EL_SATELLITE ||
7018            element == EL_PENGUIN ||
7019            element == EL_EMC_ANDROID)
7020   {
7021     int attr_x = -1, attr_y = -1;
7022
7023     if (game.all_players_gone)
7024     {
7025       attr_x = game.exit_x;
7026       attr_y = game.exit_y;
7027     }
7028     else
7029     {
7030       int i;
7031
7032       for (i = 0; i < MAX_PLAYERS; i++)
7033       {
7034         struct PlayerInfo *player = &stored_player[i];
7035         int jx = player->jx, jy = player->jy;
7036
7037         if (!player->active)
7038           continue;
7039
7040         if (attr_x == -1 ||
7041             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7042         {
7043           attr_x = jx;
7044           attr_y = jy;
7045         }
7046       }
7047     }
7048
7049     if (element == EL_ROBOT &&
7050         game.robot_wheel_x >= 0 &&
7051         game.robot_wheel_y >= 0 &&
7052         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7053          game.engine_version < VERSION_IDENT(3,1,0,0)))
7054     {
7055       attr_x = game.robot_wheel_x;
7056       attr_y = game.robot_wheel_y;
7057     }
7058
7059     if (element == EL_PENGUIN)
7060     {
7061       int i;
7062       static int xy[4][2] =
7063       {
7064         { 0, -1 },
7065         { -1, 0 },
7066         { +1, 0 },
7067         { 0, +1 }
7068       };
7069
7070       for (i = 0; i < NUM_DIRECTIONS; i++)
7071       {
7072         int ex = x + xy[i][0];
7073         int ey = y + xy[i][1];
7074
7075         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7076                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7077                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7078                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7079         {
7080           attr_x = ex;
7081           attr_y = ey;
7082           break;
7083         }
7084       }
7085     }
7086
7087     MovDir[x][y] = MV_NONE;
7088     if (attr_x < x)
7089       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7090     else if (attr_x > x)
7091       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7092     if (attr_y < y)
7093       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7094     else if (attr_y > y)
7095       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7096
7097     if (element == EL_ROBOT)
7098     {
7099       int newx, newy;
7100
7101       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7102         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7103       Moving2Blocked(x, y, &newx, &newy);
7104
7105       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7106         MovDelay[x][y] = 8 + 8 * !RND(3);
7107       else
7108         MovDelay[x][y] = 16;
7109     }
7110     else if (element == EL_PENGUIN)
7111     {
7112       int newx, newy;
7113
7114       MovDelay[x][y] = 1;
7115
7116       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7117       {
7118         boolean first_horiz = RND(2);
7119         int new_move_dir = MovDir[x][y];
7120
7121         MovDir[x][y] =
7122           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7123         Moving2Blocked(x, y, &newx, &newy);
7124
7125         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7126           return;
7127
7128         MovDir[x][y] =
7129           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7130         Moving2Blocked(x, y, &newx, &newy);
7131
7132         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7133           return;
7134
7135         MovDir[x][y] = old_move_dir;
7136         return;
7137       }
7138     }
7139     else if (element == EL_SATELLITE)
7140     {
7141       int newx, newy;
7142
7143       MovDelay[x][y] = 1;
7144
7145       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7146       {
7147         boolean first_horiz = RND(2);
7148         int new_move_dir = MovDir[x][y];
7149
7150         MovDir[x][y] =
7151           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7152         Moving2Blocked(x, y, &newx, &newy);
7153
7154         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7155           return;
7156
7157         MovDir[x][y] =
7158           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7159         Moving2Blocked(x, y, &newx, &newy);
7160
7161         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7162           return;
7163
7164         MovDir[x][y] = old_move_dir;
7165         return;
7166       }
7167     }
7168     else if (element == EL_EMC_ANDROID)
7169     {
7170       static int check_pos[16] =
7171       {
7172         -1,             //  0 => (invalid)
7173         7,              //  1 => MV_LEFT
7174         3,              //  2 => MV_RIGHT
7175         -1,             //  3 => (invalid)
7176         1,              //  4 =>            MV_UP
7177         0,              //  5 => MV_LEFT  | MV_UP
7178         2,              //  6 => MV_RIGHT | MV_UP
7179         -1,             //  7 => (invalid)
7180         5,              //  8 =>            MV_DOWN
7181         6,              //  9 => MV_LEFT  | MV_DOWN
7182         4,              // 10 => MV_RIGHT | MV_DOWN
7183         -1,             // 11 => (invalid)
7184         -1,             // 12 => (invalid)
7185         -1,             // 13 => (invalid)
7186         -1,             // 14 => (invalid)
7187         -1,             // 15 => (invalid)
7188       };
7189       static struct
7190       {
7191         int dx, dy;
7192         int dir;
7193       } check_xy[8] =
7194       {
7195         { -1, -1,       MV_LEFT  | MV_UP   },
7196         {  0, -1,                  MV_UP   },
7197         { +1, -1,       MV_RIGHT | MV_UP   },
7198         { +1,  0,       MV_RIGHT           },
7199         { +1, +1,       MV_RIGHT | MV_DOWN },
7200         {  0, +1,                  MV_DOWN },
7201         { -1, +1,       MV_LEFT  | MV_DOWN },
7202         { -1,  0,       MV_LEFT            },
7203       };
7204       int start_pos, check_order;
7205       boolean can_clone = FALSE;
7206       int i;
7207
7208       // check if there is any free field around current position
7209       for (i = 0; i < 8; i++)
7210       {
7211         int newx = x + check_xy[i].dx;
7212         int newy = y + check_xy[i].dy;
7213
7214         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7215         {
7216           can_clone = TRUE;
7217
7218           break;
7219         }
7220       }
7221
7222       if (can_clone)            // randomly find an element to clone
7223       {
7224         can_clone = FALSE;
7225
7226         start_pos = check_pos[RND(8)];
7227         check_order = (RND(2) ? -1 : +1);
7228
7229         for (i = 0; i < 8; i++)
7230         {
7231           int pos_raw = start_pos + i * check_order;
7232           int pos = (pos_raw + 8) % 8;
7233           int newx = x + check_xy[pos].dx;
7234           int newy = y + check_xy[pos].dy;
7235
7236           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7237           {
7238             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7239             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7240
7241             Store[x][y] = Tile[newx][newy];
7242
7243             can_clone = TRUE;
7244
7245             break;
7246           }
7247         }
7248       }
7249
7250       if (can_clone)            // randomly find a direction to move
7251       {
7252         can_clone = FALSE;
7253
7254         start_pos = check_pos[RND(8)];
7255         check_order = (RND(2) ? -1 : +1);
7256
7257         for (i = 0; i < 8; i++)
7258         {
7259           int pos_raw = start_pos + i * check_order;
7260           int pos = (pos_raw + 8) % 8;
7261           int newx = x + check_xy[pos].dx;
7262           int newy = y + check_xy[pos].dy;
7263           int new_move_dir = check_xy[pos].dir;
7264
7265           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7266           {
7267             MovDir[x][y] = new_move_dir;
7268             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7269
7270             can_clone = TRUE;
7271
7272             break;
7273           }
7274         }
7275       }
7276
7277       if (can_clone)            // cloning and moving successful
7278         return;
7279
7280       // cannot clone -- try to move towards player
7281
7282       start_pos = check_pos[MovDir[x][y] & 0x0f];
7283       check_order = (RND(2) ? -1 : +1);
7284
7285       for (i = 0; i < 3; i++)
7286       {
7287         // first check start_pos, then previous/next or (next/previous) pos
7288         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7289         int pos = (pos_raw + 8) % 8;
7290         int newx = x + check_xy[pos].dx;
7291         int newy = y + check_xy[pos].dy;
7292         int new_move_dir = check_xy[pos].dir;
7293
7294         if (IS_PLAYER(newx, newy))
7295           break;
7296
7297         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7298         {
7299           MovDir[x][y] = new_move_dir;
7300           MovDelay[x][y] = level.android_move_time * 8 + 1;
7301
7302           break;
7303         }
7304       }
7305     }
7306   }
7307   else if (move_pattern == MV_TURNING_LEFT ||
7308            move_pattern == MV_TURNING_RIGHT ||
7309            move_pattern == MV_TURNING_LEFT_RIGHT ||
7310            move_pattern == MV_TURNING_RIGHT_LEFT ||
7311            move_pattern == MV_TURNING_RANDOM ||
7312            move_pattern == MV_ALL_DIRECTIONS)
7313   {
7314     boolean can_turn_left =
7315       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7316     boolean can_turn_right =
7317       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7318
7319     if (element_info[element].move_stepsize == 0)       // "not moving"
7320       return;
7321
7322     if (move_pattern == MV_TURNING_LEFT)
7323       MovDir[x][y] = left_dir;
7324     else if (move_pattern == MV_TURNING_RIGHT)
7325       MovDir[x][y] = right_dir;
7326     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7327       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7328     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7329       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7330     else if (move_pattern == MV_TURNING_RANDOM)
7331       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7332                       can_turn_right && !can_turn_left ? right_dir :
7333                       RND(2) ? left_dir : right_dir);
7334     else if (can_turn_left && can_turn_right)
7335       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7336     else if (can_turn_left)
7337       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7338     else if (can_turn_right)
7339       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7340     else
7341       MovDir[x][y] = back_dir;
7342
7343     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7344   }
7345   else if (move_pattern == MV_HORIZONTAL ||
7346            move_pattern == MV_VERTICAL)
7347   {
7348     if (move_pattern & old_move_dir)
7349       MovDir[x][y] = back_dir;
7350     else if (move_pattern == MV_HORIZONTAL)
7351       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7352     else if (move_pattern == MV_VERTICAL)
7353       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7354
7355     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7356   }
7357   else if (move_pattern & MV_ANY_DIRECTION)
7358   {
7359     MovDir[x][y] = move_pattern;
7360     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7361   }
7362   else if (move_pattern & MV_WIND_DIRECTION)
7363   {
7364     MovDir[x][y] = game.wind_direction;
7365     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7366   }
7367   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7368   {
7369     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7370       MovDir[x][y] = left_dir;
7371     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7372       MovDir[x][y] = right_dir;
7373
7374     if (MovDir[x][y] != old_move_dir)
7375       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7376   }
7377   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7378   {
7379     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7380       MovDir[x][y] = right_dir;
7381     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7382       MovDir[x][y] = left_dir;
7383
7384     if (MovDir[x][y] != old_move_dir)
7385       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7386   }
7387   else if (move_pattern == MV_TOWARDS_PLAYER ||
7388            move_pattern == MV_AWAY_FROM_PLAYER)
7389   {
7390     int attr_x = -1, attr_y = -1;
7391     int newx, newy;
7392     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7393
7394     if (game.all_players_gone)
7395     {
7396       attr_x = game.exit_x;
7397       attr_y = game.exit_y;
7398     }
7399     else
7400     {
7401       int i;
7402
7403       for (i = 0; i < MAX_PLAYERS; i++)
7404       {
7405         struct PlayerInfo *player = &stored_player[i];
7406         int jx = player->jx, jy = player->jy;
7407
7408         if (!player->active)
7409           continue;
7410
7411         if (attr_x == -1 ||
7412             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7413         {
7414           attr_x = jx;
7415           attr_y = jy;
7416         }
7417       }
7418     }
7419
7420     MovDir[x][y] = MV_NONE;
7421     if (attr_x < x)
7422       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7423     else if (attr_x > x)
7424       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7425     if (attr_y < y)
7426       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7427     else if (attr_y > y)
7428       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7429
7430     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7431
7432     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7433     {
7434       boolean first_horiz = RND(2);
7435       int new_move_dir = MovDir[x][y];
7436
7437       if (element_info[element].move_stepsize == 0)     // "not moving"
7438       {
7439         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7440         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7441
7442         return;
7443       }
7444
7445       MovDir[x][y] =
7446         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7447       Moving2Blocked(x, y, &newx, &newy);
7448
7449       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7450         return;
7451
7452       MovDir[x][y] =
7453         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7454       Moving2Blocked(x, y, &newx, &newy);
7455
7456       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7457         return;
7458
7459       MovDir[x][y] = old_move_dir;
7460     }
7461   }
7462   else if (move_pattern == MV_WHEN_PUSHED ||
7463            move_pattern == MV_WHEN_DROPPED)
7464   {
7465     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7466       MovDir[x][y] = MV_NONE;
7467
7468     MovDelay[x][y] = 0;
7469   }
7470   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7471   {
7472     static int test_xy[7][2] =
7473     {
7474       { 0, -1 },
7475       { -1, 0 },
7476       { +1, 0 },
7477       { 0, +1 },
7478       { 0, -1 },
7479       { -1, 0 },
7480       { +1, 0 },
7481     };
7482     static int test_dir[7] =
7483     {
7484       MV_UP,
7485       MV_LEFT,
7486       MV_RIGHT,
7487       MV_DOWN,
7488       MV_UP,
7489       MV_LEFT,
7490       MV_RIGHT,
7491     };
7492     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7493     int move_preference = -1000000;     // start with very low preference
7494     int new_move_dir = MV_NONE;
7495     int start_test = RND(4);
7496     int i;
7497
7498     for (i = 0; i < NUM_DIRECTIONS; i++)
7499     {
7500       int move_dir = test_dir[start_test + i];
7501       int move_dir_preference;
7502
7503       xx = x + test_xy[start_test + i][0];
7504       yy = y + test_xy[start_test + i][1];
7505
7506       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7507           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7508       {
7509         new_move_dir = move_dir;
7510
7511         break;
7512       }
7513
7514       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7515         continue;
7516
7517       move_dir_preference = -1 * RunnerVisit[xx][yy];
7518       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7519         move_dir_preference = PlayerVisit[xx][yy];
7520
7521       if (move_dir_preference > move_preference)
7522       {
7523         // prefer field that has not been visited for the longest time
7524         move_preference = move_dir_preference;
7525         new_move_dir = move_dir;
7526       }
7527       else if (move_dir_preference == move_preference &&
7528                move_dir == old_move_dir)
7529       {
7530         // prefer last direction when all directions are preferred equally
7531         move_preference = move_dir_preference;
7532         new_move_dir = move_dir;
7533       }
7534     }
7535
7536     MovDir[x][y] = new_move_dir;
7537     if (old_move_dir != new_move_dir)
7538       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7539   }
7540 }
7541
7542 static void TurnRound(int x, int y)
7543 {
7544   int direction = MovDir[x][y];
7545
7546   TurnRoundExt(x, y);
7547
7548   GfxDir[x][y] = MovDir[x][y];
7549
7550   if (direction != MovDir[x][y])
7551     GfxFrame[x][y] = 0;
7552
7553   if (MovDelay[x][y])
7554     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7555
7556   ResetGfxFrame(x, y);
7557 }
7558
7559 static boolean JustBeingPushed(int x, int y)
7560 {
7561   int i;
7562
7563   for (i = 0; i < MAX_PLAYERS; i++)
7564   {
7565     struct PlayerInfo *player = &stored_player[i];
7566
7567     if (player->active && player->is_pushing && player->MovPos)
7568     {
7569       int next_jx = player->jx + (player->jx - player->last_jx);
7570       int next_jy = player->jy + (player->jy - player->last_jy);
7571
7572       if (x == next_jx && y == next_jy)
7573         return TRUE;
7574     }
7575   }
7576
7577   return FALSE;
7578 }
7579
7580 static void StartMoving(int x, int y)
7581 {
7582   boolean started_moving = FALSE;       // some elements can fall _and_ move
7583   int element = Tile[x][y];
7584
7585   if (Stop[x][y])
7586     return;
7587
7588   if (MovDelay[x][y] == 0)
7589     GfxAction[x][y] = ACTION_DEFAULT;
7590
7591   if (CAN_FALL(element) && y < lev_fieldy - 1)
7592   {
7593     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7594         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7595       if (JustBeingPushed(x, y))
7596         return;
7597
7598     if (element == EL_QUICKSAND_FULL)
7599     {
7600       if (IS_FREE(x, y + 1))
7601       {
7602         InitMovingField(x, y, MV_DOWN);
7603         started_moving = TRUE;
7604
7605         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7606 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7607         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7608           Store[x][y] = EL_ROCK;
7609 #else
7610         Store[x][y] = EL_ROCK;
7611 #endif
7612
7613         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7614       }
7615       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7616       {
7617         if (!MovDelay[x][y])
7618         {
7619           MovDelay[x][y] = TILEY + 1;
7620
7621           ResetGfxAnimation(x, y);
7622           ResetGfxAnimation(x, y + 1);
7623         }
7624
7625         if (MovDelay[x][y])
7626         {
7627           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7628           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7629
7630           MovDelay[x][y]--;
7631           if (MovDelay[x][y])
7632             return;
7633         }
7634
7635         Tile[x][y] = EL_QUICKSAND_EMPTY;
7636         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7637         Store[x][y + 1] = Store[x][y];
7638         Store[x][y] = 0;
7639
7640         PlayLevelSoundAction(x, y, ACTION_FILLING);
7641       }
7642       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7643       {
7644         if (!MovDelay[x][y])
7645         {
7646           MovDelay[x][y] = TILEY + 1;
7647
7648           ResetGfxAnimation(x, y);
7649           ResetGfxAnimation(x, y + 1);
7650         }
7651
7652         if (MovDelay[x][y])
7653         {
7654           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7655           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7656
7657           MovDelay[x][y]--;
7658           if (MovDelay[x][y])
7659             return;
7660         }
7661
7662         Tile[x][y] = EL_QUICKSAND_EMPTY;
7663         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7664         Store[x][y + 1] = Store[x][y];
7665         Store[x][y] = 0;
7666
7667         PlayLevelSoundAction(x, y, ACTION_FILLING);
7668       }
7669     }
7670     else if (element == EL_QUICKSAND_FAST_FULL)
7671     {
7672       if (IS_FREE(x, y + 1))
7673       {
7674         InitMovingField(x, y, MV_DOWN);
7675         started_moving = TRUE;
7676
7677         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7678 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7679         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7680           Store[x][y] = EL_ROCK;
7681 #else
7682         Store[x][y] = EL_ROCK;
7683 #endif
7684
7685         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7686       }
7687       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7688       {
7689         if (!MovDelay[x][y])
7690         {
7691           MovDelay[x][y] = TILEY + 1;
7692
7693           ResetGfxAnimation(x, y);
7694           ResetGfxAnimation(x, y + 1);
7695         }
7696
7697         if (MovDelay[x][y])
7698         {
7699           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7700           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7701
7702           MovDelay[x][y]--;
7703           if (MovDelay[x][y])
7704             return;
7705         }
7706
7707         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7708         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7709         Store[x][y + 1] = Store[x][y];
7710         Store[x][y] = 0;
7711
7712         PlayLevelSoundAction(x, y, ACTION_FILLING);
7713       }
7714       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7715       {
7716         if (!MovDelay[x][y])
7717         {
7718           MovDelay[x][y] = TILEY + 1;
7719
7720           ResetGfxAnimation(x, y);
7721           ResetGfxAnimation(x, y + 1);
7722         }
7723
7724         if (MovDelay[x][y])
7725         {
7726           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7727           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7728
7729           MovDelay[x][y]--;
7730           if (MovDelay[x][y])
7731             return;
7732         }
7733
7734         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7735         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7736         Store[x][y + 1] = Store[x][y];
7737         Store[x][y] = 0;
7738
7739         PlayLevelSoundAction(x, y, ACTION_FILLING);
7740       }
7741     }
7742     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7743              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7744     {
7745       InitMovingField(x, y, MV_DOWN);
7746       started_moving = TRUE;
7747
7748       Tile[x][y] = EL_QUICKSAND_FILLING;
7749       Store[x][y] = element;
7750
7751       PlayLevelSoundAction(x, y, ACTION_FILLING);
7752     }
7753     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7754              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7755     {
7756       InitMovingField(x, y, MV_DOWN);
7757       started_moving = TRUE;
7758
7759       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7760       Store[x][y] = element;
7761
7762       PlayLevelSoundAction(x, y, ACTION_FILLING);
7763     }
7764     else if (element == EL_MAGIC_WALL_FULL)
7765     {
7766       if (IS_FREE(x, y + 1))
7767       {
7768         InitMovingField(x, y, MV_DOWN);
7769         started_moving = TRUE;
7770
7771         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7772         Store[x][y] = EL_CHANGED(Store[x][y]);
7773       }
7774       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7775       {
7776         if (!MovDelay[x][y])
7777           MovDelay[x][y] = TILEY / 4 + 1;
7778
7779         if (MovDelay[x][y])
7780         {
7781           MovDelay[x][y]--;
7782           if (MovDelay[x][y])
7783             return;
7784         }
7785
7786         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7787         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7788         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7789         Store[x][y] = 0;
7790       }
7791     }
7792     else if (element == EL_BD_MAGIC_WALL_FULL)
7793     {
7794       if (IS_FREE(x, y + 1))
7795       {
7796         InitMovingField(x, y, MV_DOWN);
7797         started_moving = TRUE;
7798
7799         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7800         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7801       }
7802       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7803       {
7804         if (!MovDelay[x][y])
7805           MovDelay[x][y] = TILEY / 4 + 1;
7806
7807         if (MovDelay[x][y])
7808         {
7809           MovDelay[x][y]--;
7810           if (MovDelay[x][y])
7811             return;
7812         }
7813
7814         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7815         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7816         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7817         Store[x][y] = 0;
7818       }
7819     }
7820     else if (element == EL_DC_MAGIC_WALL_FULL)
7821     {
7822       if (IS_FREE(x, y + 1))
7823       {
7824         InitMovingField(x, y, MV_DOWN);
7825         started_moving = TRUE;
7826
7827         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7828         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7829       }
7830       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7831       {
7832         if (!MovDelay[x][y])
7833           MovDelay[x][y] = TILEY / 4 + 1;
7834
7835         if (MovDelay[x][y])
7836         {
7837           MovDelay[x][y]--;
7838           if (MovDelay[x][y])
7839             return;
7840         }
7841
7842         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7843         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7844         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7845         Store[x][y] = 0;
7846       }
7847     }
7848     else if ((CAN_PASS_MAGIC_WALL(element) &&
7849               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7850                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7851              (CAN_PASS_DC_MAGIC_WALL(element) &&
7852               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7853
7854     {
7855       InitMovingField(x, y, MV_DOWN);
7856       started_moving = TRUE;
7857
7858       Tile[x][y] =
7859         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7860          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7861          EL_DC_MAGIC_WALL_FILLING);
7862       Store[x][y] = element;
7863     }
7864     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7865     {
7866       SplashAcid(x, y + 1);
7867
7868       InitMovingField(x, y, MV_DOWN);
7869       started_moving = TRUE;
7870
7871       Store[x][y] = EL_ACID;
7872     }
7873     else if (
7874              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7875               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7876              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7877               CAN_FALL(element) && WasJustFalling[x][y] &&
7878               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7879
7880              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7881               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7882               (Tile[x][y + 1] == EL_BLOCKED)))
7883     {
7884       /* this is needed for a special case not covered by calling "Impact()"
7885          from "ContinueMoving()": if an element moves to a tile directly below
7886          another element which was just falling on that tile (which was empty
7887          in the previous frame), the falling element above would just stop
7888          instead of smashing the element below (in previous version, the above
7889          element was just checked for "moving" instead of "falling", resulting
7890          in incorrect smashes caused by horizontal movement of the above
7891          element; also, the case of the player being the element to smash was
7892          simply not covered here... :-/ ) */
7893
7894       CheckCollision[x][y] = 0;
7895       CheckImpact[x][y] = 0;
7896
7897       Impact(x, y);
7898     }
7899     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7900     {
7901       if (MovDir[x][y] == MV_NONE)
7902       {
7903         InitMovingField(x, y, MV_DOWN);
7904         started_moving = TRUE;
7905       }
7906     }
7907     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7908     {
7909       if (WasJustFalling[x][y]) // prevent animation from being restarted
7910         MovDir[x][y] = MV_DOWN;
7911
7912       InitMovingField(x, y, MV_DOWN);
7913       started_moving = TRUE;
7914     }
7915     else if (element == EL_AMOEBA_DROP)
7916     {
7917       Tile[x][y] = EL_AMOEBA_GROWING;
7918       Store[x][y] = EL_AMOEBA_WET;
7919     }
7920     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7921               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7922              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7923              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7924     {
7925       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7926                                 (IS_FREE(x - 1, y + 1) ||
7927                                  Tile[x - 1][y + 1] == EL_ACID));
7928       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7929                                 (IS_FREE(x + 1, y + 1) ||
7930                                  Tile[x + 1][y + 1] == EL_ACID));
7931       boolean can_fall_any  = (can_fall_left || can_fall_right);
7932       boolean can_fall_both = (can_fall_left && can_fall_right);
7933       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
7934
7935       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7936       {
7937         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7938           can_fall_right = FALSE;
7939         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7940           can_fall_left = FALSE;
7941         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7942           can_fall_right = FALSE;
7943         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7944           can_fall_left = FALSE;
7945
7946         can_fall_any  = (can_fall_left || can_fall_right);
7947         can_fall_both = FALSE;
7948       }
7949
7950       if (can_fall_both)
7951       {
7952         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7953           can_fall_right = FALSE;       // slip down on left side
7954         else
7955           can_fall_left = !(can_fall_right = RND(2));
7956
7957         can_fall_both = FALSE;
7958       }
7959
7960       if (can_fall_any)
7961       {
7962         // if not determined otherwise, prefer left side for slipping down
7963         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7964         started_moving = TRUE;
7965       }
7966     }
7967     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
7968     {
7969       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7970       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7971       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
7972       int belt_dir = game.belt_dir[belt_nr];
7973
7974       if ((belt_dir == MV_LEFT  && left_is_free) ||
7975           (belt_dir == MV_RIGHT && right_is_free))
7976       {
7977         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7978
7979         InitMovingField(x, y, belt_dir);
7980         started_moving = TRUE;
7981
7982         Pushed[x][y] = TRUE;
7983         Pushed[nextx][y] = TRUE;
7984
7985         GfxAction[x][y] = ACTION_DEFAULT;
7986       }
7987       else
7988       {
7989         MovDir[x][y] = 0;       // if element was moving, stop it
7990       }
7991     }
7992   }
7993
7994   // not "else if" because of elements that can fall and move (EL_SPRING)
7995   if (CAN_MOVE(element) && !started_moving)
7996   {
7997     int move_pattern = element_info[element].move_pattern;
7998     int newx, newy;
7999
8000     Moving2Blocked(x, y, &newx, &newy);
8001
8002     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8003       return;
8004
8005     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8006         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8007     {
8008       WasJustMoving[x][y] = 0;
8009       CheckCollision[x][y] = 0;
8010
8011       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8012
8013       if (Tile[x][y] != element)        // element has changed
8014         return;
8015     }
8016
8017     if (!MovDelay[x][y])        // start new movement phase
8018     {
8019       // all objects that can change their move direction after each step
8020       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8021
8022       if (element != EL_YAMYAM &&
8023           element != EL_DARK_YAMYAM &&
8024           element != EL_PACMAN &&
8025           !(move_pattern & MV_ANY_DIRECTION) &&
8026           move_pattern != MV_TURNING_LEFT &&
8027           move_pattern != MV_TURNING_RIGHT &&
8028           move_pattern != MV_TURNING_LEFT_RIGHT &&
8029           move_pattern != MV_TURNING_RIGHT_LEFT &&
8030           move_pattern != MV_TURNING_RANDOM)
8031       {
8032         TurnRound(x, y);
8033
8034         if (MovDelay[x][y] && (element == EL_BUG ||
8035                                element == EL_SPACESHIP ||
8036                                element == EL_SP_SNIKSNAK ||
8037                                element == EL_SP_ELECTRON ||
8038                                element == EL_MOLE))
8039           TEST_DrawLevelField(x, y);
8040       }
8041     }
8042
8043     if (MovDelay[x][y])         // wait some time before next movement
8044     {
8045       MovDelay[x][y]--;
8046
8047       if (element == EL_ROBOT ||
8048           element == EL_YAMYAM ||
8049           element == EL_DARK_YAMYAM)
8050       {
8051         DrawLevelElementAnimationIfNeeded(x, y, element);
8052         PlayLevelSoundAction(x, y, ACTION_WAITING);
8053       }
8054       else if (element == EL_SP_ELECTRON)
8055         DrawLevelElementAnimationIfNeeded(x, y, element);
8056       else if (element == EL_DRAGON)
8057       {
8058         int i;
8059         int dir = MovDir[x][y];
8060         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8061         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8062         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8063                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8064                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8065                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8066         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8067
8068         GfxAction[x][y] = ACTION_ATTACKING;
8069
8070         if (IS_PLAYER(x, y))
8071           DrawPlayerField(x, y);
8072         else
8073           TEST_DrawLevelField(x, y);
8074
8075         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8076
8077         for (i = 1; i <= 3; i++)
8078         {
8079           int xx = x + i * dx;
8080           int yy = y + i * dy;
8081           int sx = SCREENX(xx);
8082           int sy = SCREENY(yy);
8083           int flame_graphic = graphic + (i - 1);
8084
8085           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8086             break;
8087
8088           if (MovDelay[x][y])
8089           {
8090             int flamed = MovingOrBlocked2Element(xx, yy);
8091
8092             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8093               Bang(xx, yy);
8094             else
8095               RemoveMovingField(xx, yy);
8096
8097             ChangeDelay[xx][yy] = 0;
8098
8099             Tile[xx][yy] = EL_FLAMES;
8100
8101             if (IN_SCR_FIELD(sx, sy))
8102             {
8103               TEST_DrawLevelFieldCrumbled(xx, yy);
8104               DrawGraphic(sx, sy, flame_graphic, frame);
8105             }
8106           }
8107           else
8108           {
8109             if (Tile[xx][yy] == EL_FLAMES)
8110               Tile[xx][yy] = EL_EMPTY;
8111             TEST_DrawLevelField(xx, yy);
8112           }
8113         }
8114       }
8115
8116       if (MovDelay[x][y])       // element still has to wait some time
8117       {
8118         PlayLevelSoundAction(x, y, ACTION_WAITING);
8119
8120         return;
8121       }
8122     }
8123
8124     // now make next step
8125
8126     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8127
8128     if (DONT_COLLIDE_WITH(element) &&
8129         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8130         !PLAYER_ENEMY_PROTECTED(newx, newy))
8131     {
8132       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8133
8134       return;
8135     }
8136
8137     else if (CAN_MOVE_INTO_ACID(element) &&
8138              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8139              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8140              (MovDir[x][y] == MV_DOWN ||
8141               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8142     {
8143       SplashAcid(newx, newy);
8144       Store[x][y] = EL_ACID;
8145     }
8146     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8147     {
8148       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8149           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8150           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8151           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8152       {
8153         RemoveField(x, y);
8154         TEST_DrawLevelField(x, y);
8155
8156         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8157         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8158           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8159
8160         game.friends_still_needed--;
8161         if (!game.friends_still_needed &&
8162             !game.GameOver &&
8163             game.all_players_gone)
8164           LevelSolved();
8165
8166         return;
8167       }
8168       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8169       {
8170         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8171           TEST_DrawLevelField(newx, newy);
8172         else
8173           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8174       }
8175       else if (!IS_FREE(newx, newy))
8176       {
8177         GfxAction[x][y] = ACTION_WAITING;
8178
8179         if (IS_PLAYER(x, y))
8180           DrawPlayerField(x, y);
8181         else
8182           TEST_DrawLevelField(x, y);
8183
8184         return;
8185       }
8186     }
8187     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8188     {
8189       if (IS_FOOD_PIG(Tile[newx][newy]))
8190       {
8191         if (IS_MOVING(newx, newy))
8192           RemoveMovingField(newx, newy);
8193         else
8194         {
8195           Tile[newx][newy] = EL_EMPTY;
8196           TEST_DrawLevelField(newx, newy);
8197         }
8198
8199         PlayLevelSound(x, y, SND_PIG_DIGGING);
8200       }
8201       else if (!IS_FREE(newx, newy))
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_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8212     {
8213       if (Store[x][y] != EL_EMPTY)
8214       {
8215         boolean can_clone = FALSE;
8216         int xx, yy;
8217
8218         // check if element to clone is still there
8219         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8220         {
8221           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8222           {
8223             can_clone = TRUE;
8224
8225             break;
8226           }
8227         }
8228
8229         // cannot clone or target field not free anymore -- do not clone
8230         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8231           Store[x][y] = EL_EMPTY;
8232       }
8233
8234       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8235       {
8236         if (IS_MV_DIAGONAL(MovDir[x][y]))
8237         {
8238           int diagonal_move_dir = MovDir[x][y];
8239           int stored = Store[x][y];
8240           int change_delay = 8;
8241           int graphic;
8242
8243           // android is moving diagonally
8244
8245           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8246
8247           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8248           GfxElement[x][y] = EL_EMC_ANDROID;
8249           GfxAction[x][y] = ACTION_SHRINKING;
8250           GfxDir[x][y] = diagonal_move_dir;
8251           ChangeDelay[x][y] = change_delay;
8252
8253           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8254                                    GfxDir[x][y]);
8255
8256           DrawLevelGraphicAnimation(x, y, graphic);
8257           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8258
8259           if (Tile[newx][newy] == EL_ACID)
8260           {
8261             SplashAcid(newx, newy);
8262
8263             return;
8264           }
8265
8266           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8267
8268           Store[newx][newy] = EL_EMC_ANDROID;
8269           GfxElement[newx][newy] = EL_EMC_ANDROID;
8270           GfxAction[newx][newy] = ACTION_GROWING;
8271           GfxDir[newx][newy] = diagonal_move_dir;
8272           ChangeDelay[newx][newy] = change_delay;
8273
8274           graphic = el_act_dir2img(GfxElement[newx][newy],
8275                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8276
8277           DrawLevelGraphicAnimation(newx, newy, graphic);
8278           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8279
8280           return;
8281         }
8282         else
8283         {
8284           Tile[newx][newy] = EL_EMPTY;
8285           TEST_DrawLevelField(newx, newy);
8286
8287           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8288         }
8289       }
8290       else if (!IS_FREE(newx, newy))
8291       {
8292         return;
8293       }
8294     }
8295     else if (IS_CUSTOM_ELEMENT(element) &&
8296              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8297     {
8298       if (!DigFieldByCE(newx, newy, element))
8299         return;
8300
8301       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8302       {
8303         RunnerVisit[x][y] = FrameCounter;
8304         PlayerVisit[x][y] /= 8;         // expire player visit path
8305       }
8306     }
8307     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8308     {
8309       if (!IS_FREE(newx, newy))
8310       {
8311         if (IS_PLAYER(x, y))
8312           DrawPlayerField(x, y);
8313         else
8314           TEST_DrawLevelField(x, y);
8315
8316         return;
8317       }
8318       else
8319       {
8320         boolean wanna_flame = !RND(10);
8321         int dx = newx - x, dy = newy - y;
8322         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8323         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8324         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8325                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8326         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8327                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8328
8329         if ((wanna_flame ||
8330              IS_CLASSIC_ENEMY(element1) ||
8331              IS_CLASSIC_ENEMY(element2)) &&
8332             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8333             element1 != EL_FLAMES && element2 != EL_FLAMES)
8334         {
8335           ResetGfxAnimation(x, y);
8336           GfxAction[x][y] = ACTION_ATTACKING;
8337
8338           if (IS_PLAYER(x, y))
8339             DrawPlayerField(x, y);
8340           else
8341             TEST_DrawLevelField(x, y);
8342
8343           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8344
8345           MovDelay[x][y] = 50;
8346
8347           Tile[newx][newy] = EL_FLAMES;
8348           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8349             Tile[newx1][newy1] = EL_FLAMES;
8350           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8351             Tile[newx2][newy2] = EL_FLAMES;
8352
8353           return;
8354         }
8355       }
8356     }
8357     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8358              Tile[newx][newy] == EL_DIAMOND)
8359     {
8360       if (IS_MOVING(newx, newy))
8361         RemoveMovingField(newx, newy);
8362       else
8363       {
8364         Tile[newx][newy] = EL_EMPTY;
8365         TEST_DrawLevelField(newx, newy);
8366       }
8367
8368       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8369     }
8370     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8371              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8372     {
8373       if (AmoebaNr[newx][newy])
8374       {
8375         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8376         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8377             Tile[newx][newy] == EL_BD_AMOEBA)
8378           AmoebaCnt[AmoebaNr[newx][newy]]--;
8379       }
8380
8381       if (IS_MOVING(newx, newy))
8382       {
8383         RemoveMovingField(newx, newy);
8384       }
8385       else
8386       {
8387         Tile[newx][newy] = EL_EMPTY;
8388         TEST_DrawLevelField(newx, newy);
8389       }
8390
8391       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8392     }
8393     else if ((element == EL_PACMAN || element == EL_MOLE)
8394              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8395     {
8396       if (AmoebaNr[newx][newy])
8397       {
8398         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8399         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8400             Tile[newx][newy] == EL_BD_AMOEBA)
8401           AmoebaCnt[AmoebaNr[newx][newy]]--;
8402       }
8403
8404       if (element == EL_MOLE)
8405       {
8406         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8407         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8408
8409         ResetGfxAnimation(x, y);
8410         GfxAction[x][y] = ACTION_DIGGING;
8411         TEST_DrawLevelField(x, y);
8412
8413         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8414
8415         return;                         // wait for shrinking amoeba
8416       }
8417       else      // element == EL_PACMAN
8418       {
8419         Tile[newx][newy] = EL_EMPTY;
8420         TEST_DrawLevelField(newx, newy);
8421         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8422       }
8423     }
8424     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8425              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8426               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8427     {
8428       // wait for shrinking amoeba to completely disappear
8429       return;
8430     }
8431     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8432     {
8433       // object was running against a wall
8434
8435       TurnRound(x, y);
8436
8437       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8438         DrawLevelElementAnimation(x, y, element);
8439
8440       if (DONT_TOUCH(element))
8441         TestIfBadThingTouchesPlayer(x, y);
8442
8443       return;
8444     }
8445
8446     InitMovingField(x, y, MovDir[x][y]);
8447
8448     PlayLevelSoundAction(x, y, ACTION_MOVING);
8449   }
8450
8451   if (MovDir[x][y])
8452     ContinueMoving(x, y);
8453 }
8454
8455 void ContinueMoving(int x, int y)
8456 {
8457   int element = Tile[x][y];
8458   struct ElementInfo *ei = &element_info[element];
8459   int direction = MovDir[x][y];
8460   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8461   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8462   int newx = x + dx, newy = y + dy;
8463   int stored = Store[x][y];
8464   int stored_new = Store[newx][newy];
8465   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8466   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8467   boolean last_line = (newy == lev_fieldy - 1);
8468
8469   MovPos[x][y] += getElementMoveStepsize(x, y);
8470
8471   if (pushed_by_player) // special case: moving object pushed by player
8472     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8473
8474   if (ABS(MovPos[x][y]) < TILEX)
8475   {
8476     TEST_DrawLevelField(x, y);
8477
8478     return;     // element is still moving
8479   }
8480
8481   // element reached destination field
8482
8483   Tile[x][y] = EL_EMPTY;
8484   Tile[newx][newy] = element;
8485   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8486
8487   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8488   {
8489     element = Tile[newx][newy] = EL_ACID;
8490   }
8491   else if (element == EL_MOLE)
8492   {
8493     Tile[x][y] = EL_SAND;
8494
8495     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8496   }
8497   else if (element == EL_QUICKSAND_FILLING)
8498   {
8499     element = Tile[newx][newy] = get_next_element(element);
8500     Store[newx][newy] = Store[x][y];
8501   }
8502   else if (element == EL_QUICKSAND_EMPTYING)
8503   {
8504     Tile[x][y] = get_next_element(element);
8505     element = Tile[newx][newy] = Store[x][y];
8506   }
8507   else if (element == EL_QUICKSAND_FAST_FILLING)
8508   {
8509     element = Tile[newx][newy] = get_next_element(element);
8510     Store[newx][newy] = Store[x][y];
8511   }
8512   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8513   {
8514     Tile[x][y] = get_next_element(element);
8515     element = Tile[newx][newy] = Store[x][y];
8516   }
8517   else if (element == EL_MAGIC_WALL_FILLING)
8518   {
8519     element = Tile[newx][newy] = get_next_element(element);
8520     if (!game.magic_wall_active)
8521       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8522     Store[newx][newy] = Store[x][y];
8523   }
8524   else if (element == EL_MAGIC_WALL_EMPTYING)
8525   {
8526     Tile[x][y] = get_next_element(element);
8527     if (!game.magic_wall_active)
8528       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8529     element = Tile[newx][newy] = Store[x][y];
8530
8531     InitField(newx, newy, FALSE);
8532   }
8533   else if (element == EL_BD_MAGIC_WALL_FILLING)
8534   {
8535     element = Tile[newx][newy] = get_next_element(element);
8536     if (!game.magic_wall_active)
8537       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8538     Store[newx][newy] = Store[x][y];
8539   }
8540   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8541   {
8542     Tile[x][y] = get_next_element(element);
8543     if (!game.magic_wall_active)
8544       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8545     element = Tile[newx][newy] = Store[x][y];
8546
8547     InitField(newx, newy, FALSE);
8548   }
8549   else if (element == EL_DC_MAGIC_WALL_FILLING)
8550   {
8551     element = Tile[newx][newy] = get_next_element(element);
8552     if (!game.magic_wall_active)
8553       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8554     Store[newx][newy] = Store[x][y];
8555   }
8556   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8557   {
8558     Tile[x][y] = get_next_element(element);
8559     if (!game.magic_wall_active)
8560       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8561     element = Tile[newx][newy] = Store[x][y];
8562
8563     InitField(newx, newy, FALSE);
8564   }
8565   else if (element == EL_AMOEBA_DROPPING)
8566   {
8567     Tile[x][y] = get_next_element(element);
8568     element = Tile[newx][newy] = Store[x][y];
8569   }
8570   else if (element == EL_SOKOBAN_OBJECT)
8571   {
8572     if (Back[x][y])
8573       Tile[x][y] = Back[x][y];
8574
8575     if (Back[newx][newy])
8576       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8577
8578     Back[x][y] = Back[newx][newy] = 0;
8579   }
8580
8581   Store[x][y] = EL_EMPTY;
8582   MovPos[x][y] = 0;
8583   MovDir[x][y] = 0;
8584   MovDelay[x][y] = 0;
8585
8586   MovDelay[newx][newy] = 0;
8587
8588   if (CAN_CHANGE_OR_HAS_ACTION(element))
8589   {
8590     // copy element change control values to new field
8591     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8592     ChangePage[newx][newy]  = ChangePage[x][y];
8593     ChangeCount[newx][newy] = ChangeCount[x][y];
8594     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8595   }
8596
8597   CustomValue[newx][newy] = CustomValue[x][y];
8598
8599   ChangeDelay[x][y] = 0;
8600   ChangePage[x][y] = -1;
8601   ChangeCount[x][y] = 0;
8602   ChangeEvent[x][y] = -1;
8603
8604   CustomValue[x][y] = 0;
8605
8606   // copy animation control values to new field
8607   GfxFrame[newx][newy]  = GfxFrame[x][y];
8608   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8609   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8610   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8611
8612   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8613
8614   // some elements can leave other elements behind after moving
8615   if (ei->move_leave_element != EL_EMPTY &&
8616       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8617       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8618   {
8619     int move_leave_element = ei->move_leave_element;
8620
8621     // this makes it possible to leave the removed element again
8622     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8623       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8624
8625     Tile[x][y] = move_leave_element;
8626
8627     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8628       MovDir[x][y] = direction;
8629
8630     InitField(x, y, FALSE);
8631
8632     if (GFX_CRUMBLED(Tile[x][y]))
8633       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8634
8635     if (ELEM_IS_PLAYER(move_leave_element))
8636       RelocatePlayer(x, y, move_leave_element);
8637   }
8638
8639   // do this after checking for left-behind element
8640   ResetGfxAnimation(x, y);      // reset animation values for old field
8641
8642   if (!CAN_MOVE(element) ||
8643       (CAN_FALL(element) && direction == MV_DOWN &&
8644        (element == EL_SPRING ||
8645         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8646         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8647     GfxDir[x][y] = MovDir[newx][newy] = 0;
8648
8649   TEST_DrawLevelField(x, y);
8650   TEST_DrawLevelField(newx, newy);
8651
8652   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8653
8654   // prevent pushed element from moving on in pushed direction
8655   if (pushed_by_player && CAN_MOVE(element) &&
8656       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8657       !(element_info[element].move_pattern & direction))
8658     TurnRound(newx, newy);
8659
8660   // prevent elements on conveyor belt from moving on in last direction
8661   if (pushed_by_conveyor && CAN_FALL(element) &&
8662       direction & MV_HORIZONTAL)
8663     MovDir[newx][newy] = 0;
8664
8665   if (!pushed_by_player)
8666   {
8667     int nextx = newx + dx, nexty = newy + dy;
8668     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8669
8670     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8671
8672     if (CAN_FALL(element) && direction == MV_DOWN)
8673       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8674
8675     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8676       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8677
8678     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8679       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8680   }
8681
8682   if (DONT_TOUCH(element))      // object may be nasty to player or others
8683   {
8684     TestIfBadThingTouchesPlayer(newx, newy);
8685     TestIfBadThingTouchesFriend(newx, newy);
8686
8687     if (!IS_CUSTOM_ELEMENT(element))
8688       TestIfBadThingTouchesOtherBadThing(newx, newy);
8689   }
8690   else if (element == EL_PENGUIN)
8691     TestIfFriendTouchesBadThing(newx, newy);
8692
8693   if (DONT_GET_HIT_BY(element))
8694   {
8695     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8696   }
8697
8698   // give the player one last chance (one more frame) to move away
8699   if (CAN_FALL(element) && direction == MV_DOWN &&
8700       (last_line || (!IS_FREE(x, newy + 1) &&
8701                      (!IS_PLAYER(x, newy + 1) ||
8702                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8703     Impact(x, newy);
8704
8705   if (pushed_by_player && !game.use_change_when_pushing_bug)
8706   {
8707     int push_side = MV_DIR_OPPOSITE(direction);
8708     struct PlayerInfo *player = PLAYERINFO(x, y);
8709
8710     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8711                                player->index_bit, push_side);
8712     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8713                                         player->index_bit, push_side);
8714   }
8715
8716   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8717     MovDelay[newx][newy] = 1;
8718
8719   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8720
8721   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8722   TestIfElementHitsCustomElement(newx, newy, direction);
8723   TestIfPlayerTouchesCustomElement(newx, newy);
8724   TestIfElementTouchesCustomElement(newx, newy);
8725
8726   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8727       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8728     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8729                              MV_DIR_OPPOSITE(direction));
8730 }
8731
8732 int AmoebaNeighbourNr(int ax, int ay)
8733 {
8734   int i;
8735   int element = Tile[ax][ay];
8736   int group_nr = 0;
8737   static int xy[4][2] =
8738   {
8739     { 0, -1 },
8740     { -1, 0 },
8741     { +1, 0 },
8742     { 0, +1 }
8743   };
8744
8745   for (i = 0; i < NUM_DIRECTIONS; i++)
8746   {
8747     int x = ax + xy[i][0];
8748     int y = ay + xy[i][1];
8749
8750     if (!IN_LEV_FIELD(x, y))
8751       continue;
8752
8753     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8754       group_nr = AmoebaNr[x][y];
8755   }
8756
8757   return group_nr;
8758 }
8759
8760 static void AmoebaMerge(int ax, int ay)
8761 {
8762   int i, x, y, xx, yy;
8763   int new_group_nr = AmoebaNr[ax][ay];
8764   static int xy[4][2] =
8765   {
8766     { 0, -1 },
8767     { -1, 0 },
8768     { +1, 0 },
8769     { 0, +1 }
8770   };
8771
8772   if (new_group_nr == 0)
8773     return;
8774
8775   for (i = 0; i < NUM_DIRECTIONS; i++)
8776   {
8777     x = ax + xy[i][0];
8778     y = ay + xy[i][1];
8779
8780     if (!IN_LEV_FIELD(x, y))
8781       continue;
8782
8783     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8784          Tile[x][y] == EL_BD_AMOEBA ||
8785          Tile[x][y] == EL_AMOEBA_DEAD) &&
8786         AmoebaNr[x][y] != new_group_nr)
8787     {
8788       int old_group_nr = AmoebaNr[x][y];
8789
8790       if (old_group_nr == 0)
8791         return;
8792
8793       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8794       AmoebaCnt[old_group_nr] = 0;
8795       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8796       AmoebaCnt2[old_group_nr] = 0;
8797
8798       SCAN_PLAYFIELD(xx, yy)
8799       {
8800         if (AmoebaNr[xx][yy] == old_group_nr)
8801           AmoebaNr[xx][yy] = new_group_nr;
8802       }
8803     }
8804   }
8805 }
8806
8807 void AmoebaToDiamond(int ax, int ay)
8808 {
8809   int i, x, y;
8810
8811   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8812   {
8813     int group_nr = AmoebaNr[ax][ay];
8814
8815 #ifdef DEBUG
8816     if (group_nr == 0)
8817     {
8818       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8819       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8820
8821       return;
8822     }
8823 #endif
8824
8825     SCAN_PLAYFIELD(x, y)
8826     {
8827       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8828       {
8829         AmoebaNr[x][y] = 0;
8830         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8831       }
8832     }
8833
8834     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8835                             SND_AMOEBA_TURNING_TO_GEM :
8836                             SND_AMOEBA_TURNING_TO_ROCK));
8837     Bang(ax, ay);
8838   }
8839   else
8840   {
8841     static int xy[4][2] =
8842     {
8843       { 0, -1 },
8844       { -1, 0 },
8845       { +1, 0 },
8846       { 0, +1 }
8847     };
8848
8849     for (i = 0; i < NUM_DIRECTIONS; i++)
8850     {
8851       x = ax + xy[i][0];
8852       y = ay + xy[i][1];
8853
8854       if (!IN_LEV_FIELD(x, y))
8855         continue;
8856
8857       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8858       {
8859         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8860                               SND_AMOEBA_TURNING_TO_GEM :
8861                               SND_AMOEBA_TURNING_TO_ROCK));
8862         Bang(x, y);
8863       }
8864     }
8865   }
8866 }
8867
8868 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8869 {
8870   int x, y;
8871   int group_nr = AmoebaNr[ax][ay];
8872   boolean done = FALSE;
8873
8874 #ifdef DEBUG
8875   if (group_nr == 0)
8876   {
8877     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8878     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8879
8880     return;
8881   }
8882 #endif
8883
8884   SCAN_PLAYFIELD(x, y)
8885   {
8886     if (AmoebaNr[x][y] == group_nr &&
8887         (Tile[x][y] == EL_AMOEBA_DEAD ||
8888          Tile[x][y] == EL_BD_AMOEBA ||
8889          Tile[x][y] == EL_AMOEBA_GROWING))
8890     {
8891       AmoebaNr[x][y] = 0;
8892       Tile[x][y] = new_element;
8893       InitField(x, y, FALSE);
8894       TEST_DrawLevelField(x, y);
8895       done = TRUE;
8896     }
8897   }
8898
8899   if (done)
8900     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8901                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8902                             SND_BD_AMOEBA_TURNING_TO_GEM));
8903 }
8904
8905 static void AmoebaGrowing(int x, int y)
8906 {
8907   static unsigned int sound_delay = 0;
8908   static unsigned int sound_delay_value = 0;
8909
8910   if (!MovDelay[x][y])          // start new growing cycle
8911   {
8912     MovDelay[x][y] = 7;
8913
8914     if (DelayReached(&sound_delay, sound_delay_value))
8915     {
8916       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8917       sound_delay_value = 30;
8918     }
8919   }
8920
8921   if (MovDelay[x][y])           // wait some time before growing bigger
8922   {
8923     MovDelay[x][y]--;
8924     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8925     {
8926       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8927                                            6 - MovDelay[x][y]);
8928
8929       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8930     }
8931
8932     if (!MovDelay[x][y])
8933     {
8934       Tile[x][y] = Store[x][y];
8935       Store[x][y] = 0;
8936       TEST_DrawLevelField(x, y);
8937     }
8938   }
8939 }
8940
8941 static void AmoebaShrinking(int x, int y)
8942 {
8943   static unsigned int sound_delay = 0;
8944   static unsigned int sound_delay_value = 0;
8945
8946   if (!MovDelay[x][y])          // start new shrinking cycle
8947   {
8948     MovDelay[x][y] = 7;
8949
8950     if (DelayReached(&sound_delay, sound_delay_value))
8951       sound_delay_value = 30;
8952   }
8953
8954   if (MovDelay[x][y])           // wait some time before shrinking
8955   {
8956     MovDelay[x][y]--;
8957     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8958     {
8959       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8960                                            6 - MovDelay[x][y]);
8961
8962       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8963     }
8964
8965     if (!MovDelay[x][y])
8966     {
8967       Tile[x][y] = EL_EMPTY;
8968       TEST_DrawLevelField(x, y);
8969
8970       // don't let mole enter this field in this cycle;
8971       // (give priority to objects falling to this field from above)
8972       Stop[x][y] = TRUE;
8973     }
8974   }
8975 }
8976
8977 static void AmoebaReproduce(int ax, int ay)
8978 {
8979   int i;
8980   int element = Tile[ax][ay];
8981   int graphic = el2img(element);
8982   int newax = ax, neway = ay;
8983   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8984   static int xy[4][2] =
8985   {
8986     { 0, -1 },
8987     { -1, 0 },
8988     { +1, 0 },
8989     { 0, +1 }
8990   };
8991
8992   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8993   {
8994     Tile[ax][ay] = EL_AMOEBA_DEAD;
8995     TEST_DrawLevelField(ax, ay);
8996     return;
8997   }
8998
8999   if (IS_ANIMATED(graphic))
9000     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9001
9002   if (!MovDelay[ax][ay])        // start making new amoeba field
9003     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9004
9005   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9006   {
9007     MovDelay[ax][ay]--;
9008     if (MovDelay[ax][ay])
9009       return;
9010   }
9011
9012   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9013   {
9014     int start = RND(4);
9015     int x = ax + xy[start][0];
9016     int y = ay + xy[start][1];
9017
9018     if (!IN_LEV_FIELD(x, y))
9019       return;
9020
9021     if (IS_FREE(x, y) ||
9022         CAN_GROW_INTO(Tile[x][y]) ||
9023         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9024         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9025     {
9026       newax = x;
9027       neway = y;
9028     }
9029
9030     if (newax == ax && neway == ay)
9031       return;
9032   }
9033   else                          // normal or "filled" (BD style) amoeba
9034   {
9035     int start = RND(4);
9036     boolean waiting_for_player = FALSE;
9037
9038     for (i = 0; i < NUM_DIRECTIONS; i++)
9039     {
9040       int j = (start + i) % 4;
9041       int x = ax + xy[j][0];
9042       int y = ay + xy[j][1];
9043
9044       if (!IN_LEV_FIELD(x, y))
9045         continue;
9046
9047       if (IS_FREE(x, y) ||
9048           CAN_GROW_INTO(Tile[x][y]) ||
9049           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9050           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9051       {
9052         newax = x;
9053         neway = y;
9054         break;
9055       }
9056       else if (IS_PLAYER(x, y))
9057         waiting_for_player = TRUE;
9058     }
9059
9060     if (newax == ax && neway == ay)             // amoeba cannot grow
9061     {
9062       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9063       {
9064         Tile[ax][ay] = EL_AMOEBA_DEAD;
9065         TEST_DrawLevelField(ax, ay);
9066         AmoebaCnt[AmoebaNr[ax][ay]]--;
9067
9068         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9069         {
9070           if (element == EL_AMOEBA_FULL)
9071             AmoebaToDiamond(ax, ay);
9072           else if (element == EL_BD_AMOEBA)
9073             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9074         }
9075       }
9076       return;
9077     }
9078     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9079     {
9080       // amoeba gets larger by growing in some direction
9081
9082       int new_group_nr = AmoebaNr[ax][ay];
9083
9084 #ifdef DEBUG
9085   if (new_group_nr == 0)
9086   {
9087     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9088           newax, neway);
9089     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9090
9091     return;
9092   }
9093 #endif
9094
9095       AmoebaNr[newax][neway] = new_group_nr;
9096       AmoebaCnt[new_group_nr]++;
9097       AmoebaCnt2[new_group_nr]++;
9098
9099       // if amoeba touches other amoeba(s) after growing, unify them
9100       AmoebaMerge(newax, neway);
9101
9102       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9103       {
9104         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9105         return;
9106       }
9107     }
9108   }
9109
9110   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9111       (neway == lev_fieldy - 1 && newax != ax))
9112   {
9113     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9114     Store[newax][neway] = element;
9115   }
9116   else if (neway == ay || element == EL_EMC_DRIPPER)
9117   {
9118     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9119
9120     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9121   }
9122   else
9123   {
9124     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9125     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9126     Store[ax][ay] = EL_AMOEBA_DROP;
9127     ContinueMoving(ax, ay);
9128     return;
9129   }
9130
9131   TEST_DrawLevelField(newax, neway);
9132 }
9133
9134 static void Life(int ax, int ay)
9135 {
9136   int x1, y1, x2, y2;
9137   int life_time = 40;
9138   int element = Tile[ax][ay];
9139   int graphic = el2img(element);
9140   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9141                          level.biomaze);
9142   boolean changed = FALSE;
9143
9144   if (IS_ANIMATED(graphic))
9145     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9146
9147   if (Stop[ax][ay])
9148     return;
9149
9150   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9151     MovDelay[ax][ay] = life_time;
9152
9153   if (MovDelay[ax][ay])         // wait some time before next cycle
9154   {
9155     MovDelay[ax][ay]--;
9156     if (MovDelay[ax][ay])
9157       return;
9158   }
9159
9160   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9161   {
9162     int xx = ax+x1, yy = ay+y1;
9163     int old_element = Tile[xx][yy];
9164     int num_neighbours = 0;
9165
9166     if (!IN_LEV_FIELD(xx, yy))
9167       continue;
9168
9169     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9170     {
9171       int x = xx+x2, y = yy+y2;
9172
9173       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9174         continue;
9175
9176       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9177       boolean is_neighbour = FALSE;
9178
9179       if (level.use_life_bugs)
9180         is_neighbour =
9181           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9182            (IS_FREE(x, y)                             &&  Stop[x][y]));
9183       else
9184         is_neighbour =
9185           (Last[x][y] == element || is_player_cell);
9186
9187       if (is_neighbour)
9188         num_neighbours++;
9189     }
9190
9191     boolean is_free = FALSE;
9192
9193     if (level.use_life_bugs)
9194       is_free = (IS_FREE(xx, yy));
9195     else
9196       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9197
9198     if (xx == ax && yy == ay)           // field in the middle
9199     {
9200       if (num_neighbours < life_parameter[0] ||
9201           num_neighbours > life_parameter[1])
9202       {
9203         Tile[xx][yy] = EL_EMPTY;
9204         if (Tile[xx][yy] != old_element)
9205           TEST_DrawLevelField(xx, yy);
9206         Stop[xx][yy] = TRUE;
9207         changed = TRUE;
9208       }
9209     }
9210     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9211     {                                   // free border field
9212       if (num_neighbours >= life_parameter[2] &&
9213           num_neighbours <= life_parameter[3])
9214       {
9215         Tile[xx][yy] = element;
9216         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9217         if (Tile[xx][yy] != old_element)
9218           TEST_DrawLevelField(xx, yy);
9219         Stop[xx][yy] = TRUE;
9220         changed = TRUE;
9221       }
9222     }
9223   }
9224
9225   if (changed)
9226     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9227                    SND_GAME_OF_LIFE_GROWING);
9228 }
9229
9230 static void InitRobotWheel(int x, int y)
9231 {
9232   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9233 }
9234
9235 static void RunRobotWheel(int x, int y)
9236 {
9237   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9238 }
9239
9240 static void StopRobotWheel(int x, int y)
9241 {
9242   if (game.robot_wheel_x == x &&
9243       game.robot_wheel_y == y)
9244   {
9245     game.robot_wheel_x = -1;
9246     game.robot_wheel_y = -1;
9247     game.robot_wheel_active = FALSE;
9248   }
9249 }
9250
9251 static void InitTimegateWheel(int x, int y)
9252 {
9253   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9254 }
9255
9256 static void RunTimegateWheel(int x, int y)
9257 {
9258   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9259 }
9260
9261 static void InitMagicBallDelay(int x, int y)
9262 {
9263   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9264 }
9265
9266 static void ActivateMagicBall(int bx, int by)
9267 {
9268   int x, y;
9269
9270   if (level.ball_random)
9271   {
9272     int pos_border = RND(8);    // select one of the eight border elements
9273     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9274     int xx = pos_content % 3;
9275     int yy = pos_content / 3;
9276
9277     x = bx - 1 + xx;
9278     y = by - 1 + yy;
9279
9280     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9281       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9282   }
9283   else
9284   {
9285     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9286     {
9287       int xx = x - bx + 1;
9288       int yy = y - by + 1;
9289
9290       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9291         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9292     }
9293   }
9294
9295   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9296 }
9297
9298 static void CheckExit(int x, int y)
9299 {
9300   if (game.gems_still_needed > 0 ||
9301       game.sokoban_fields_still_needed > 0 ||
9302       game.sokoban_objects_still_needed > 0 ||
9303       game.lights_still_needed > 0)
9304   {
9305     int element = Tile[x][y];
9306     int graphic = el2img(element);
9307
9308     if (IS_ANIMATED(graphic))
9309       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9310
9311     return;
9312   }
9313
9314   // do not re-open exit door closed after last player
9315   if (game.all_players_gone)
9316     return;
9317
9318   Tile[x][y] = EL_EXIT_OPENING;
9319
9320   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9321 }
9322
9323 static void CheckExitEM(int x, int y)
9324 {
9325   if (game.gems_still_needed > 0 ||
9326       game.sokoban_fields_still_needed > 0 ||
9327       game.sokoban_objects_still_needed > 0 ||
9328       game.lights_still_needed > 0)
9329   {
9330     int element = Tile[x][y];
9331     int graphic = el2img(element);
9332
9333     if (IS_ANIMATED(graphic))
9334       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9335
9336     return;
9337   }
9338
9339   // do not re-open exit door closed after last player
9340   if (game.all_players_gone)
9341     return;
9342
9343   Tile[x][y] = EL_EM_EXIT_OPENING;
9344
9345   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9346 }
9347
9348 static void CheckExitSteel(int x, int y)
9349 {
9350   if (game.gems_still_needed > 0 ||
9351       game.sokoban_fields_still_needed > 0 ||
9352       game.sokoban_objects_still_needed > 0 ||
9353       game.lights_still_needed > 0)
9354   {
9355     int element = Tile[x][y];
9356     int graphic = el2img(element);
9357
9358     if (IS_ANIMATED(graphic))
9359       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9360
9361     return;
9362   }
9363
9364   // do not re-open exit door closed after last player
9365   if (game.all_players_gone)
9366     return;
9367
9368   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9369
9370   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9371 }
9372
9373 static void CheckExitSteelEM(int x, int y)
9374 {
9375   if (game.gems_still_needed > 0 ||
9376       game.sokoban_fields_still_needed > 0 ||
9377       game.sokoban_objects_still_needed > 0 ||
9378       game.lights_still_needed > 0)
9379   {
9380     int element = Tile[x][y];
9381     int graphic = el2img(element);
9382
9383     if (IS_ANIMATED(graphic))
9384       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9385
9386     return;
9387   }
9388
9389   // do not re-open exit door closed after last player
9390   if (game.all_players_gone)
9391     return;
9392
9393   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9394
9395   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9396 }
9397
9398 static void CheckExitSP(int x, int y)
9399 {
9400   if (game.gems_still_needed > 0)
9401   {
9402     int element = Tile[x][y];
9403     int graphic = el2img(element);
9404
9405     if (IS_ANIMATED(graphic))
9406       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9407
9408     return;
9409   }
9410
9411   // do not re-open exit door closed after last player
9412   if (game.all_players_gone)
9413     return;
9414
9415   Tile[x][y] = EL_SP_EXIT_OPENING;
9416
9417   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9418 }
9419
9420 static void CloseAllOpenTimegates(void)
9421 {
9422   int x, y;
9423
9424   SCAN_PLAYFIELD(x, y)
9425   {
9426     int element = Tile[x][y];
9427
9428     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9429     {
9430       Tile[x][y] = EL_TIMEGATE_CLOSING;
9431
9432       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9433     }
9434   }
9435 }
9436
9437 static void DrawTwinkleOnField(int x, int y)
9438 {
9439   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9440     return;
9441
9442   if (Tile[x][y] == EL_BD_DIAMOND)
9443     return;
9444
9445   if (MovDelay[x][y] == 0)      // next animation frame
9446     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9447
9448   if (MovDelay[x][y] != 0)      // wait some time before next frame
9449   {
9450     MovDelay[x][y]--;
9451
9452     DrawLevelElementAnimation(x, y, Tile[x][y]);
9453
9454     if (MovDelay[x][y] != 0)
9455     {
9456       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9457                                            10 - MovDelay[x][y]);
9458
9459       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9460     }
9461   }
9462 }
9463
9464 static void MauerWaechst(int x, int y)
9465 {
9466   int delay = 6;
9467
9468   if (!MovDelay[x][y])          // next animation frame
9469     MovDelay[x][y] = 3 * delay;
9470
9471   if (MovDelay[x][y])           // wait some time before next frame
9472   {
9473     MovDelay[x][y]--;
9474
9475     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9476     {
9477       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9478       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9479
9480       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9481     }
9482
9483     if (!MovDelay[x][y])
9484     {
9485       if (MovDir[x][y] == MV_LEFT)
9486       {
9487         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9488           TEST_DrawLevelField(x - 1, y);
9489       }
9490       else if (MovDir[x][y] == MV_RIGHT)
9491       {
9492         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9493           TEST_DrawLevelField(x + 1, y);
9494       }
9495       else if (MovDir[x][y] == MV_UP)
9496       {
9497         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9498           TEST_DrawLevelField(x, y - 1);
9499       }
9500       else
9501       {
9502         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9503           TEST_DrawLevelField(x, y + 1);
9504       }
9505
9506       Tile[x][y] = Store[x][y];
9507       Store[x][y] = 0;
9508       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9509       TEST_DrawLevelField(x, y);
9510     }
9511   }
9512 }
9513
9514 static void MauerAbleger(int ax, int ay)
9515 {
9516   int element = Tile[ax][ay];
9517   int graphic = el2img(element);
9518   boolean oben_frei = FALSE, unten_frei = FALSE;
9519   boolean links_frei = FALSE, rechts_frei = FALSE;
9520   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9521   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9522   boolean new_wall = FALSE;
9523
9524   if (IS_ANIMATED(graphic))
9525     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9526
9527   if (!MovDelay[ax][ay])        // start building new wall
9528     MovDelay[ax][ay] = 6;
9529
9530   if (MovDelay[ax][ay])         // wait some time before building new wall
9531   {
9532     MovDelay[ax][ay]--;
9533     if (MovDelay[ax][ay])
9534       return;
9535   }
9536
9537   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9538     oben_frei = TRUE;
9539   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9540     unten_frei = TRUE;
9541   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9542     links_frei = TRUE;
9543   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9544     rechts_frei = TRUE;
9545
9546   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9547       element == EL_EXPANDABLE_WALL_ANY)
9548   {
9549     if (oben_frei)
9550     {
9551       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9552       Store[ax][ay-1] = element;
9553       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9554       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9555         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9556                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9557       new_wall = TRUE;
9558     }
9559     if (unten_frei)
9560     {
9561       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9562       Store[ax][ay+1] = element;
9563       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9564       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9565         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9566                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9567       new_wall = TRUE;
9568     }
9569   }
9570
9571   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9572       element == EL_EXPANDABLE_WALL_ANY ||
9573       element == EL_EXPANDABLE_WALL ||
9574       element == EL_BD_EXPANDABLE_WALL)
9575   {
9576     if (links_frei)
9577     {
9578       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9579       Store[ax-1][ay] = element;
9580       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9581       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9582         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9583                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9584       new_wall = TRUE;
9585     }
9586
9587     if (rechts_frei)
9588     {
9589       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9590       Store[ax+1][ay] = element;
9591       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9592       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9593         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9594                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9595       new_wall = TRUE;
9596     }
9597   }
9598
9599   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9600     TEST_DrawLevelField(ax, ay);
9601
9602   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9603     oben_massiv = TRUE;
9604   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9605     unten_massiv = TRUE;
9606   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9607     links_massiv = TRUE;
9608   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9609     rechts_massiv = TRUE;
9610
9611   if (((oben_massiv && unten_massiv) ||
9612        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9613        element == EL_EXPANDABLE_WALL) &&
9614       ((links_massiv && rechts_massiv) ||
9615        element == EL_EXPANDABLE_WALL_VERTICAL))
9616     Tile[ax][ay] = EL_WALL;
9617
9618   if (new_wall)
9619     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9620 }
9621
9622 static void MauerAblegerStahl(int ax, int ay)
9623 {
9624   int element = Tile[ax][ay];
9625   int graphic = el2img(element);
9626   boolean oben_frei = FALSE, unten_frei = FALSE;
9627   boolean links_frei = FALSE, rechts_frei = FALSE;
9628   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9629   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9630   boolean new_wall = FALSE;
9631
9632   if (IS_ANIMATED(graphic))
9633     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9634
9635   if (!MovDelay[ax][ay])        // start building new wall
9636     MovDelay[ax][ay] = 6;
9637
9638   if (MovDelay[ax][ay])         // wait some time before building new wall
9639   {
9640     MovDelay[ax][ay]--;
9641     if (MovDelay[ax][ay])
9642       return;
9643   }
9644
9645   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9646     oben_frei = TRUE;
9647   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9648     unten_frei = TRUE;
9649   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9650     links_frei = TRUE;
9651   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9652     rechts_frei = TRUE;
9653
9654   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9655       element == EL_EXPANDABLE_STEELWALL_ANY)
9656   {
9657     if (oben_frei)
9658     {
9659       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9660       Store[ax][ay-1] = element;
9661       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9662       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9663         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9664                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9665       new_wall = TRUE;
9666     }
9667     if (unten_frei)
9668     {
9669       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9670       Store[ax][ay+1] = element;
9671       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9672       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9673         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9674                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9675       new_wall = TRUE;
9676     }
9677   }
9678
9679   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9680       element == EL_EXPANDABLE_STEELWALL_ANY)
9681   {
9682     if (links_frei)
9683     {
9684       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9685       Store[ax-1][ay] = element;
9686       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9687       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9688         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9689                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9690       new_wall = TRUE;
9691     }
9692
9693     if (rechts_frei)
9694     {
9695       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9696       Store[ax+1][ay] = element;
9697       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9698       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9699         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9700                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9701       new_wall = TRUE;
9702     }
9703   }
9704
9705   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9706     oben_massiv = TRUE;
9707   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9708     unten_massiv = TRUE;
9709   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9710     links_massiv = TRUE;
9711   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9712     rechts_massiv = TRUE;
9713
9714   if (((oben_massiv && unten_massiv) ||
9715        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9716       ((links_massiv && rechts_massiv) ||
9717        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9718     Tile[ax][ay] = EL_STEELWALL;
9719
9720   if (new_wall)
9721     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9722 }
9723
9724 static void CheckForDragon(int x, int y)
9725 {
9726   int i, j;
9727   boolean dragon_found = FALSE;
9728   static int xy[4][2] =
9729   {
9730     { 0, -1 },
9731     { -1, 0 },
9732     { +1, 0 },
9733     { 0, +1 }
9734   };
9735
9736   for (i = 0; i < NUM_DIRECTIONS; i++)
9737   {
9738     for (j = 0; j < 4; j++)
9739     {
9740       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9741
9742       if (IN_LEV_FIELD(xx, yy) &&
9743           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9744       {
9745         if (Tile[xx][yy] == EL_DRAGON)
9746           dragon_found = TRUE;
9747       }
9748       else
9749         break;
9750     }
9751   }
9752
9753   if (!dragon_found)
9754   {
9755     for (i = 0; i < NUM_DIRECTIONS; i++)
9756     {
9757       for (j = 0; j < 3; j++)
9758       {
9759         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9760   
9761         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9762         {
9763           Tile[xx][yy] = EL_EMPTY;
9764           TEST_DrawLevelField(xx, yy);
9765         }
9766         else
9767           break;
9768       }
9769     }
9770   }
9771 }
9772
9773 static void InitBuggyBase(int x, int y)
9774 {
9775   int element = Tile[x][y];
9776   int activating_delay = FRAMES_PER_SECOND / 4;
9777
9778   ChangeDelay[x][y] =
9779     (element == EL_SP_BUGGY_BASE ?
9780      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9781      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9782      activating_delay :
9783      element == EL_SP_BUGGY_BASE_ACTIVE ?
9784      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9785 }
9786
9787 static void WarnBuggyBase(int x, int y)
9788 {
9789   int i;
9790   static int xy[4][2] =
9791   {
9792     { 0, -1 },
9793     { -1, 0 },
9794     { +1, 0 },
9795     { 0, +1 }
9796   };
9797
9798   for (i = 0; i < NUM_DIRECTIONS; i++)
9799   {
9800     int xx = x + xy[i][0];
9801     int yy = y + xy[i][1];
9802
9803     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9804     {
9805       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9806
9807       break;
9808     }
9809   }
9810 }
9811
9812 static void InitTrap(int x, int y)
9813 {
9814   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9815 }
9816
9817 static void ActivateTrap(int x, int y)
9818 {
9819   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9820 }
9821
9822 static void ChangeActiveTrap(int x, int y)
9823 {
9824   int graphic = IMG_TRAP_ACTIVE;
9825
9826   // if new animation frame was drawn, correct crumbled sand border
9827   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9828     TEST_DrawLevelFieldCrumbled(x, y);
9829 }
9830
9831 static int getSpecialActionElement(int element, int number, int base_element)
9832 {
9833   return (element != EL_EMPTY ? element :
9834           number != -1 ? base_element + number - 1 :
9835           EL_EMPTY);
9836 }
9837
9838 static int getModifiedActionNumber(int value_old, int operator, int operand,
9839                                    int value_min, int value_max)
9840 {
9841   int value_new = (operator == CA_MODE_SET      ? operand :
9842                    operator == CA_MODE_ADD      ? value_old + operand :
9843                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9844                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9845                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9846                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9847                    value_old);
9848
9849   return (value_new < value_min ? value_min :
9850           value_new > value_max ? value_max :
9851           value_new);
9852 }
9853
9854 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9855 {
9856   struct ElementInfo *ei = &element_info[element];
9857   struct ElementChangeInfo *change = &ei->change_page[page];
9858   int target_element = change->target_element;
9859   int action_type = change->action_type;
9860   int action_mode = change->action_mode;
9861   int action_arg = change->action_arg;
9862   int action_element = change->action_element;
9863   int i;
9864
9865   if (!change->has_action)
9866     return;
9867
9868   // ---------- determine action paramater values -----------------------------
9869
9870   int level_time_value =
9871     (level.time > 0 ? TimeLeft :
9872      TimePlayed);
9873
9874   int action_arg_element_raw =
9875     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9876      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9877      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9878      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9879      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9880      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9881      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9882      EL_EMPTY);
9883   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9884
9885   int action_arg_direction =
9886     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9887      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9888      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9889      change->actual_trigger_side :
9890      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9891      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9892      MV_NONE);
9893
9894   int action_arg_number_min =
9895     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9896      CA_ARG_MIN);
9897
9898   int action_arg_number_max =
9899     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9900      action_type == CA_SET_LEVEL_GEMS ? 999 :
9901      action_type == CA_SET_LEVEL_TIME ? 9999 :
9902      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9903      action_type == CA_SET_CE_VALUE ? 9999 :
9904      action_type == CA_SET_CE_SCORE ? 9999 :
9905      CA_ARG_MAX);
9906
9907   int action_arg_number_reset =
9908     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9909      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9910      action_type == CA_SET_LEVEL_TIME ? level.time :
9911      action_type == CA_SET_LEVEL_SCORE ? 0 :
9912      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9913      action_type == CA_SET_CE_SCORE ? 0 :
9914      0);
9915
9916   int action_arg_number =
9917     (action_arg <= CA_ARG_MAX ? action_arg :
9918      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9919      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9920      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9921      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9922      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9923      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9924      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9925      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9926      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9927      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9928      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9929      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9930      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9931      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9932      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9933      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9934      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9935      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9936      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9937      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9938      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9939      -1);
9940
9941   int action_arg_number_old =
9942     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9943      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9944      action_type == CA_SET_LEVEL_SCORE ? game.score :
9945      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9946      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9947      0);
9948
9949   int action_arg_number_new =
9950     getModifiedActionNumber(action_arg_number_old,
9951                             action_mode, action_arg_number,
9952                             action_arg_number_min, action_arg_number_max);
9953
9954   int trigger_player_bits =
9955     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9956      change->actual_trigger_player_bits : change->trigger_player);
9957
9958   int action_arg_player_bits =
9959     (action_arg >= CA_ARG_PLAYER_1 &&
9960      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9961      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9962      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9963      PLAYER_BITS_ANY);
9964
9965   // ---------- execute action  -----------------------------------------------
9966
9967   switch (action_type)
9968   {
9969     case CA_NO_ACTION:
9970     {
9971       return;
9972     }
9973
9974     // ---------- level actions  ----------------------------------------------
9975
9976     case CA_RESTART_LEVEL:
9977     {
9978       game.restart_level = TRUE;
9979
9980       break;
9981     }
9982
9983     case CA_SHOW_ENVELOPE:
9984     {
9985       int element = getSpecialActionElement(action_arg_element,
9986                                             action_arg_number, EL_ENVELOPE_1);
9987
9988       if (IS_ENVELOPE(element))
9989         local_player->show_envelope = element;
9990
9991       break;
9992     }
9993
9994     case CA_SET_LEVEL_TIME:
9995     {
9996       if (level.time > 0)       // only modify limited time value
9997       {
9998         TimeLeft = action_arg_number_new;
9999
10000         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10001
10002         DisplayGameControlValues();
10003
10004         if (!TimeLeft && setup.time_limit)
10005           for (i = 0; i < MAX_PLAYERS; i++)
10006             KillPlayer(&stored_player[i]);
10007       }
10008
10009       break;
10010     }
10011
10012     case CA_SET_LEVEL_SCORE:
10013     {
10014       game.score = action_arg_number_new;
10015
10016       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10017
10018       DisplayGameControlValues();
10019
10020       break;
10021     }
10022
10023     case CA_SET_LEVEL_GEMS:
10024     {
10025       game.gems_still_needed = action_arg_number_new;
10026
10027       game.snapshot.collected_item = TRUE;
10028
10029       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10030
10031       DisplayGameControlValues();
10032
10033       break;
10034     }
10035
10036     case CA_SET_LEVEL_WIND:
10037     {
10038       game.wind_direction = action_arg_direction;
10039
10040       break;
10041     }
10042
10043     case CA_SET_LEVEL_RANDOM_SEED:
10044     {
10045       // ensure that setting a new random seed while playing is predictable
10046       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10047
10048       break;
10049     }
10050
10051     // ---------- player actions  ---------------------------------------------
10052
10053     case CA_MOVE_PLAYER:
10054     case CA_MOVE_PLAYER_NEW:
10055     {
10056       // automatically move to the next field in specified direction
10057       for (i = 0; i < MAX_PLAYERS; i++)
10058         if (trigger_player_bits & (1 << i))
10059           if (action_type == CA_MOVE_PLAYER ||
10060               stored_player[i].MovPos == 0)
10061             stored_player[i].programmed_action = action_arg_direction;
10062
10063       break;
10064     }
10065
10066     case CA_EXIT_PLAYER:
10067     {
10068       for (i = 0; i < MAX_PLAYERS; i++)
10069         if (action_arg_player_bits & (1 << i))
10070           ExitPlayer(&stored_player[i]);
10071
10072       if (game.players_still_needed == 0)
10073         LevelSolved();
10074
10075       break;
10076     }
10077
10078     case CA_KILL_PLAYER:
10079     {
10080       for (i = 0; i < MAX_PLAYERS; i++)
10081         if (action_arg_player_bits & (1 << i))
10082           KillPlayer(&stored_player[i]);
10083
10084       break;
10085     }
10086
10087     case CA_SET_PLAYER_KEYS:
10088     {
10089       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10090       int element = getSpecialActionElement(action_arg_element,
10091                                             action_arg_number, EL_KEY_1);
10092
10093       if (IS_KEY(element))
10094       {
10095         for (i = 0; i < MAX_PLAYERS; i++)
10096         {
10097           if (trigger_player_bits & (1 << i))
10098           {
10099             stored_player[i].key[KEY_NR(element)] = key_state;
10100
10101             DrawGameDoorValues();
10102           }
10103         }
10104       }
10105
10106       break;
10107     }
10108
10109     case CA_SET_PLAYER_SPEED:
10110     {
10111       for (i = 0; i < MAX_PLAYERS; i++)
10112       {
10113         if (trigger_player_bits & (1 << i))
10114         {
10115           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10116
10117           if (action_arg == CA_ARG_SPEED_FASTER &&
10118               stored_player[i].cannot_move)
10119           {
10120             action_arg_number = STEPSIZE_VERY_SLOW;
10121           }
10122           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10123                    action_arg == CA_ARG_SPEED_FASTER)
10124           {
10125             action_arg_number = 2;
10126             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10127                            CA_MODE_MULTIPLY);
10128           }
10129           else if (action_arg == CA_ARG_NUMBER_RESET)
10130           {
10131             action_arg_number = level.initial_player_stepsize[i];
10132           }
10133
10134           move_stepsize =
10135             getModifiedActionNumber(move_stepsize,
10136                                     action_mode,
10137                                     action_arg_number,
10138                                     action_arg_number_min,
10139                                     action_arg_number_max);
10140
10141           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10142         }
10143       }
10144
10145       break;
10146     }
10147
10148     case CA_SET_PLAYER_SHIELD:
10149     {
10150       for (i = 0; i < MAX_PLAYERS; i++)
10151       {
10152         if (trigger_player_bits & (1 << i))
10153         {
10154           if (action_arg == CA_ARG_SHIELD_OFF)
10155           {
10156             stored_player[i].shield_normal_time_left = 0;
10157             stored_player[i].shield_deadly_time_left = 0;
10158           }
10159           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10160           {
10161             stored_player[i].shield_normal_time_left = 999999;
10162           }
10163           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10164           {
10165             stored_player[i].shield_normal_time_left = 999999;
10166             stored_player[i].shield_deadly_time_left = 999999;
10167           }
10168         }
10169       }
10170
10171       break;
10172     }
10173
10174     case CA_SET_PLAYER_GRAVITY:
10175     {
10176       for (i = 0; i < MAX_PLAYERS; i++)
10177       {
10178         if (trigger_player_bits & (1 << i))
10179         {
10180           stored_player[i].gravity =
10181             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10182              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10183              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10184              stored_player[i].gravity);
10185         }
10186       }
10187
10188       break;
10189     }
10190
10191     case CA_SET_PLAYER_ARTWORK:
10192     {
10193       for (i = 0; i < MAX_PLAYERS; i++)
10194       {
10195         if (trigger_player_bits & (1 << i))
10196         {
10197           int artwork_element = action_arg_element;
10198
10199           if (action_arg == CA_ARG_ELEMENT_RESET)
10200             artwork_element =
10201               (level.use_artwork_element[i] ? level.artwork_element[i] :
10202                stored_player[i].element_nr);
10203
10204           if (stored_player[i].artwork_element != artwork_element)
10205             stored_player[i].Frame = 0;
10206
10207           stored_player[i].artwork_element = artwork_element;
10208
10209           SetPlayerWaiting(&stored_player[i], FALSE);
10210
10211           // set number of special actions for bored and sleeping animation
10212           stored_player[i].num_special_action_bored =
10213             get_num_special_action(artwork_element,
10214                                    ACTION_BORING_1, ACTION_BORING_LAST);
10215           stored_player[i].num_special_action_sleeping =
10216             get_num_special_action(artwork_element,
10217                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10218         }
10219       }
10220
10221       break;
10222     }
10223
10224     case CA_SET_PLAYER_INVENTORY:
10225     {
10226       for (i = 0; i < MAX_PLAYERS; i++)
10227       {
10228         struct PlayerInfo *player = &stored_player[i];
10229         int j, k;
10230
10231         if (trigger_player_bits & (1 << i))
10232         {
10233           int inventory_element = action_arg_element;
10234
10235           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10236               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10237               action_arg == CA_ARG_ELEMENT_ACTION)
10238           {
10239             int element = inventory_element;
10240             int collect_count = element_info[element].collect_count_initial;
10241
10242             if (!IS_CUSTOM_ELEMENT(element))
10243               collect_count = 1;
10244
10245             if (collect_count == 0)
10246               player->inventory_infinite_element = element;
10247             else
10248               for (k = 0; k < collect_count; k++)
10249                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10250                   player->inventory_element[player->inventory_size++] =
10251                     element;
10252           }
10253           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10254                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10255                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10256           {
10257             if (player->inventory_infinite_element != EL_UNDEFINED &&
10258                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10259                                      action_arg_element_raw))
10260               player->inventory_infinite_element = EL_UNDEFINED;
10261
10262             for (k = 0, j = 0; j < player->inventory_size; j++)
10263             {
10264               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10265                                         action_arg_element_raw))
10266                 player->inventory_element[k++] = player->inventory_element[j];
10267             }
10268
10269             player->inventory_size = k;
10270           }
10271           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10272           {
10273             if (player->inventory_size > 0)
10274             {
10275               for (j = 0; j < player->inventory_size - 1; j++)
10276                 player->inventory_element[j] = player->inventory_element[j + 1];
10277
10278               player->inventory_size--;
10279             }
10280           }
10281           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10282           {
10283             if (player->inventory_size > 0)
10284               player->inventory_size--;
10285           }
10286           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10287           {
10288             player->inventory_infinite_element = EL_UNDEFINED;
10289             player->inventory_size = 0;
10290           }
10291           else if (action_arg == CA_ARG_INVENTORY_RESET)
10292           {
10293             player->inventory_infinite_element = EL_UNDEFINED;
10294             player->inventory_size = 0;
10295
10296             if (level.use_initial_inventory[i])
10297             {
10298               for (j = 0; j < level.initial_inventory_size[i]; j++)
10299               {
10300                 int element = level.initial_inventory_content[i][j];
10301                 int collect_count = element_info[element].collect_count_initial;
10302
10303                 if (!IS_CUSTOM_ELEMENT(element))
10304                   collect_count = 1;
10305
10306                 if (collect_count == 0)
10307                   player->inventory_infinite_element = element;
10308                 else
10309                   for (k = 0; k < collect_count; k++)
10310                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10311                       player->inventory_element[player->inventory_size++] =
10312                         element;
10313               }
10314             }
10315           }
10316         }
10317       }
10318
10319       break;
10320     }
10321
10322     // ---------- CE actions  -------------------------------------------------
10323
10324     case CA_SET_CE_VALUE:
10325     {
10326       int last_ce_value = CustomValue[x][y];
10327
10328       CustomValue[x][y] = action_arg_number_new;
10329
10330       if (CustomValue[x][y] != last_ce_value)
10331       {
10332         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10333         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10334
10335         if (CustomValue[x][y] == 0)
10336         {
10337           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10338           ChangeCount[x][y] = 0;        // allow at least one more change
10339
10340           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10341           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10342         }
10343       }
10344
10345       break;
10346     }
10347
10348     case CA_SET_CE_SCORE:
10349     {
10350       int last_ce_score = ei->collect_score;
10351
10352       ei->collect_score = action_arg_number_new;
10353
10354       if (ei->collect_score != last_ce_score)
10355       {
10356         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10357         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10358
10359         if (ei->collect_score == 0)
10360         {
10361           int xx, yy;
10362
10363           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10364           ChangeCount[x][y] = 0;        // allow at least one more change
10365
10366           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10367           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10368
10369           /*
10370             This is a very special case that seems to be a mixture between
10371             CheckElementChange() and CheckTriggeredElementChange(): while
10372             the first one only affects single elements that are triggered
10373             directly, the second one affects multiple elements in the playfield
10374             that are triggered indirectly by another element. This is a third
10375             case: Changing the CE score always affects multiple identical CEs,
10376             so every affected CE must be checked, not only the single CE for
10377             which the CE score was changed in the first place (as every instance
10378             of that CE shares the same CE score, and therefore also can change)!
10379           */
10380           SCAN_PLAYFIELD(xx, yy)
10381           {
10382             if (Tile[xx][yy] == element)
10383               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10384                                  CE_SCORE_GETS_ZERO);
10385           }
10386         }
10387       }
10388
10389       break;
10390     }
10391
10392     case CA_SET_CE_ARTWORK:
10393     {
10394       int artwork_element = action_arg_element;
10395       boolean reset_frame = FALSE;
10396       int xx, yy;
10397
10398       if (action_arg == CA_ARG_ELEMENT_RESET)
10399         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10400                            element);
10401
10402       if (ei->gfx_element != artwork_element)
10403         reset_frame = TRUE;
10404
10405       ei->gfx_element = artwork_element;
10406
10407       SCAN_PLAYFIELD(xx, yy)
10408       {
10409         if (Tile[xx][yy] == element)
10410         {
10411           if (reset_frame)
10412           {
10413             ResetGfxAnimation(xx, yy);
10414             ResetRandomAnimationValue(xx, yy);
10415           }
10416
10417           TEST_DrawLevelField(xx, yy);
10418         }
10419       }
10420
10421       break;
10422     }
10423
10424     // ---------- engine actions  ---------------------------------------------
10425
10426     case CA_SET_ENGINE_SCAN_MODE:
10427     {
10428       InitPlayfieldScanMode(action_arg);
10429
10430       break;
10431     }
10432
10433     default:
10434       break;
10435   }
10436 }
10437
10438 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10439 {
10440   int old_element = Tile[x][y];
10441   int new_element = GetElementFromGroupElement(element);
10442   int previous_move_direction = MovDir[x][y];
10443   int last_ce_value = CustomValue[x][y];
10444   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10445   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10446   boolean add_player_onto_element = (new_element_is_player &&
10447                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10448                                      IS_WALKABLE(old_element));
10449
10450   if (!add_player_onto_element)
10451   {
10452     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10453       RemoveMovingField(x, y);
10454     else
10455       RemoveField(x, y);
10456
10457     Tile[x][y] = new_element;
10458
10459     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10460       MovDir[x][y] = previous_move_direction;
10461
10462     if (element_info[new_element].use_last_ce_value)
10463       CustomValue[x][y] = last_ce_value;
10464
10465     InitField_WithBug1(x, y, FALSE);
10466
10467     new_element = Tile[x][y];   // element may have changed
10468
10469     ResetGfxAnimation(x, y);
10470     ResetRandomAnimationValue(x, y);
10471
10472     TEST_DrawLevelField(x, y);
10473
10474     if (GFX_CRUMBLED(new_element))
10475       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10476   }
10477
10478   // check if element under the player changes from accessible to unaccessible
10479   // (needed for special case of dropping element which then changes)
10480   // (must be checked after creating new element for walkable group elements)
10481   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10482       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10483   {
10484     Bang(x, y);
10485
10486     return;
10487   }
10488
10489   // "ChangeCount" not set yet to allow "entered by player" change one time
10490   if (new_element_is_player)
10491     RelocatePlayer(x, y, new_element);
10492
10493   if (is_change)
10494     ChangeCount[x][y]++;        // count number of changes in the same frame
10495
10496   TestIfBadThingTouchesPlayer(x, y);
10497   TestIfPlayerTouchesCustomElement(x, y);
10498   TestIfElementTouchesCustomElement(x, y);
10499 }
10500
10501 static void CreateField(int x, int y, int element)
10502 {
10503   CreateFieldExt(x, y, element, FALSE);
10504 }
10505
10506 static void CreateElementFromChange(int x, int y, int element)
10507 {
10508   element = GET_VALID_RUNTIME_ELEMENT(element);
10509
10510   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10511   {
10512     int old_element = Tile[x][y];
10513
10514     // prevent changed element from moving in same engine frame
10515     // unless both old and new element can either fall or move
10516     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10517         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10518       Stop[x][y] = TRUE;
10519   }
10520
10521   CreateFieldExt(x, y, element, TRUE);
10522 }
10523
10524 static boolean ChangeElement(int x, int y, int element, int page)
10525 {
10526   struct ElementInfo *ei = &element_info[element];
10527   struct ElementChangeInfo *change = &ei->change_page[page];
10528   int ce_value = CustomValue[x][y];
10529   int ce_score = ei->collect_score;
10530   int target_element;
10531   int old_element = Tile[x][y];
10532
10533   // always use default change event to prevent running into a loop
10534   if (ChangeEvent[x][y] == -1)
10535     ChangeEvent[x][y] = CE_DELAY;
10536
10537   if (ChangeEvent[x][y] == CE_DELAY)
10538   {
10539     // reset actual trigger element, trigger player and action element
10540     change->actual_trigger_element = EL_EMPTY;
10541     change->actual_trigger_player = EL_EMPTY;
10542     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10543     change->actual_trigger_side = CH_SIDE_NONE;
10544     change->actual_trigger_ce_value = 0;
10545     change->actual_trigger_ce_score = 0;
10546   }
10547
10548   // do not change elements more than a specified maximum number of changes
10549   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10550     return FALSE;
10551
10552   ChangeCount[x][y]++;          // count number of changes in the same frame
10553
10554   if (change->explode)
10555   {
10556     Bang(x, y);
10557
10558     return TRUE;
10559   }
10560
10561   if (change->use_target_content)
10562   {
10563     boolean complete_replace = TRUE;
10564     boolean can_replace[3][3];
10565     int xx, yy;
10566
10567     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10568     {
10569       boolean is_empty;
10570       boolean is_walkable;
10571       boolean is_diggable;
10572       boolean is_collectible;
10573       boolean is_removable;
10574       boolean is_destructible;
10575       int ex = x + xx - 1;
10576       int ey = y + yy - 1;
10577       int content_element = change->target_content.e[xx][yy];
10578       int e;
10579
10580       can_replace[xx][yy] = TRUE;
10581
10582       if (ex == x && ey == y)   // do not check changing element itself
10583         continue;
10584
10585       if (content_element == EL_EMPTY_SPACE)
10586       {
10587         can_replace[xx][yy] = FALSE;    // do not replace border with space
10588
10589         continue;
10590       }
10591
10592       if (!IN_LEV_FIELD(ex, ey))
10593       {
10594         can_replace[xx][yy] = FALSE;
10595         complete_replace = FALSE;
10596
10597         continue;
10598       }
10599
10600       e = Tile[ex][ey];
10601
10602       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10603         e = MovingOrBlocked2Element(ex, ey);
10604
10605       is_empty = (IS_FREE(ex, ey) ||
10606                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10607
10608       is_walkable     = (is_empty || IS_WALKABLE(e));
10609       is_diggable     = (is_empty || IS_DIGGABLE(e));
10610       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10611       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10612       is_removable    = (is_diggable || is_collectible);
10613
10614       can_replace[xx][yy] =
10615         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10616           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10617           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10618           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10619           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10620           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10621          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10622
10623       if (!can_replace[xx][yy])
10624         complete_replace = FALSE;
10625     }
10626
10627     if (!change->only_if_complete || complete_replace)
10628     {
10629       boolean something_has_changed = FALSE;
10630
10631       if (change->only_if_complete && change->use_random_replace &&
10632           RND(100) < change->random_percentage)
10633         return FALSE;
10634
10635       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10636       {
10637         int ex = x + xx - 1;
10638         int ey = y + yy - 1;
10639         int content_element;
10640
10641         if (can_replace[xx][yy] && (!change->use_random_replace ||
10642                                     RND(100) < change->random_percentage))
10643         {
10644           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10645             RemoveMovingField(ex, ey);
10646
10647           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10648
10649           content_element = change->target_content.e[xx][yy];
10650           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10651                                               ce_value, ce_score);
10652
10653           CreateElementFromChange(ex, ey, target_element);
10654
10655           something_has_changed = TRUE;
10656
10657           // for symmetry reasons, freeze newly created border elements
10658           if (ex != x || ey != y)
10659             Stop[ex][ey] = TRUE;        // no more moving in this frame
10660         }
10661       }
10662
10663       if (something_has_changed)
10664       {
10665         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10666         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10667       }
10668     }
10669   }
10670   else
10671   {
10672     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10673                                         ce_value, ce_score);
10674
10675     if (element == EL_DIAGONAL_GROWING ||
10676         element == EL_DIAGONAL_SHRINKING)
10677     {
10678       target_element = Store[x][y];
10679
10680       Store[x][y] = EL_EMPTY;
10681     }
10682
10683     CreateElementFromChange(x, y, target_element);
10684
10685     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10686     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10687   }
10688
10689   // this uses direct change before indirect change
10690   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10691
10692   return TRUE;
10693 }
10694
10695 static void HandleElementChange(int x, int y, int page)
10696 {
10697   int element = MovingOrBlocked2Element(x, y);
10698   struct ElementInfo *ei = &element_info[element];
10699   struct ElementChangeInfo *change = &ei->change_page[page];
10700   boolean handle_action_before_change = FALSE;
10701
10702 #ifdef DEBUG
10703   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10704       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10705   {
10706     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10707           x, y, element, element_info[element].token_name);
10708     Debug("game:playing:HandleElementChange", "This should never happen!");
10709   }
10710 #endif
10711
10712   // this can happen with classic bombs on walkable, changing elements
10713   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10714   {
10715     return;
10716   }
10717
10718   if (ChangeDelay[x][y] == 0)           // initialize element change
10719   {
10720     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10721
10722     if (change->can_change)
10723     {
10724       // !!! not clear why graphic animation should be reset at all here !!!
10725       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10726       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10727
10728       /*
10729         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10730
10731         When using an animation frame delay of 1 (this only happens with
10732         "sp_zonk.moving.left/right" in the classic graphics), the default
10733         (non-moving) animation shows wrong animation frames (while the
10734         moving animation, like "sp_zonk.moving.left/right", is correct,
10735         so this graphical bug never shows up with the classic graphics).
10736         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10737         be drawn instead of the correct frames 0,1,2,3. This is caused by
10738         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10739         an element change: First when the change delay ("ChangeDelay[][]")
10740         counter has reached zero after decrementing, then a second time in
10741         the next frame (after "GfxFrame[][]" was already incremented) when
10742         "ChangeDelay[][]" is reset to the initial delay value again.
10743
10744         This causes frame 0 to be drawn twice, while the last frame won't
10745         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10746
10747         As some animations may already be cleverly designed around this bug
10748         (at least the "Snake Bite" snake tail animation does this), it cannot
10749         simply be fixed here without breaking such existing animations.
10750         Unfortunately, it cannot easily be detected if a graphics set was
10751         designed "before" or "after" the bug was fixed. As a workaround,
10752         a new graphics set option "game.graphics_engine_version" was added
10753         to be able to specify the game's major release version for which the
10754         graphics set was designed, which can then be used to decide if the
10755         bugfix should be used (version 4 and above) or not (version 3 or
10756         below, or if no version was specified at all, as with old sets).
10757
10758         (The wrong/fixed animation frames can be tested with the test level set
10759         "test_gfxframe" and level "000", which contains a specially prepared
10760         custom element at level position (x/y) == (11/9) which uses the zonk
10761         animation mentioned above. Using "game.graphics_engine_version: 4"
10762         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10763         This can also be seen from the debug output for this test element.)
10764       */
10765
10766       // when a custom element is about to change (for example by change delay),
10767       // do not reset graphic animation when the custom element is moving
10768       if (game.graphics_engine_version < 4 &&
10769           !IS_MOVING(x, y))
10770       {
10771         ResetGfxAnimation(x, y);
10772         ResetRandomAnimationValue(x, y);
10773       }
10774
10775       if (change->pre_change_function)
10776         change->pre_change_function(x, y);
10777     }
10778   }
10779
10780   ChangeDelay[x][y]--;
10781
10782   if (ChangeDelay[x][y] != 0)           // continue element change
10783   {
10784     if (change->can_change)
10785     {
10786       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10787
10788       if (IS_ANIMATED(graphic))
10789         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10790
10791       if (change->change_function)
10792         change->change_function(x, y);
10793     }
10794   }
10795   else                                  // finish element change
10796   {
10797     if (ChangePage[x][y] != -1)         // remember page from delayed change
10798     {
10799       page = ChangePage[x][y];
10800       ChangePage[x][y] = -1;
10801
10802       change = &ei->change_page[page];
10803     }
10804
10805     if (IS_MOVING(x, y))                // never change a running system ;-)
10806     {
10807       ChangeDelay[x][y] = 1;            // try change after next move step
10808       ChangePage[x][y] = page;          // remember page to use for change
10809
10810       return;
10811     }
10812
10813     // special case: set new level random seed before changing element
10814     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10815       handle_action_before_change = TRUE;
10816
10817     if (change->has_action && handle_action_before_change)
10818       ExecuteCustomElementAction(x, y, element, page);
10819
10820     if (change->can_change)
10821     {
10822       if (ChangeElement(x, y, element, page))
10823       {
10824         if (change->post_change_function)
10825           change->post_change_function(x, y);
10826       }
10827     }
10828
10829     if (change->has_action && !handle_action_before_change)
10830       ExecuteCustomElementAction(x, y, element, page);
10831   }
10832 }
10833
10834 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10835                                               int trigger_element,
10836                                               int trigger_event,
10837                                               int trigger_player,
10838                                               int trigger_side,
10839                                               int trigger_page)
10840 {
10841   boolean change_done_any = FALSE;
10842   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10843   int i;
10844
10845   if (!(trigger_events[trigger_element][trigger_event]))
10846     return FALSE;
10847
10848   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10849
10850   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10851   {
10852     int element = EL_CUSTOM_START + i;
10853     boolean change_done = FALSE;
10854     int p;
10855
10856     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10857         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10858       continue;
10859
10860     for (p = 0; p < element_info[element].num_change_pages; p++)
10861     {
10862       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10863
10864       if (change->can_change_or_has_action &&
10865           change->has_event[trigger_event] &&
10866           change->trigger_side & trigger_side &&
10867           change->trigger_player & trigger_player &&
10868           change->trigger_page & trigger_page_bits &&
10869           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10870       {
10871         change->actual_trigger_element = trigger_element;
10872         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10873         change->actual_trigger_player_bits = trigger_player;
10874         change->actual_trigger_side = trigger_side;
10875         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10876         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10877
10878         if ((change->can_change && !change_done) || change->has_action)
10879         {
10880           int x, y;
10881
10882           SCAN_PLAYFIELD(x, y)
10883           {
10884             if (Tile[x][y] == element)
10885             {
10886               if (change->can_change && !change_done)
10887               {
10888                 // if element already changed in this frame, not only prevent
10889                 // another element change (checked in ChangeElement()), but
10890                 // also prevent additional element actions for this element
10891
10892                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10893                     !level.use_action_after_change_bug)
10894                   continue;
10895
10896                 ChangeDelay[x][y] = 1;
10897                 ChangeEvent[x][y] = trigger_event;
10898
10899                 HandleElementChange(x, y, p);
10900               }
10901               else if (change->has_action)
10902               {
10903                 // if element already changed in this frame, not only prevent
10904                 // another element change (checked in ChangeElement()), but
10905                 // also prevent additional element actions for this element
10906
10907                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10908                     !level.use_action_after_change_bug)
10909                   continue;
10910
10911                 ExecuteCustomElementAction(x, y, element, p);
10912                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10913               }
10914             }
10915           }
10916
10917           if (change->can_change)
10918           {
10919             change_done = TRUE;
10920             change_done_any = TRUE;
10921           }
10922         }
10923       }
10924     }
10925   }
10926
10927   RECURSION_LOOP_DETECTION_END();
10928
10929   return change_done_any;
10930 }
10931
10932 static boolean CheckElementChangeExt(int x, int y,
10933                                      int element,
10934                                      int trigger_element,
10935                                      int trigger_event,
10936                                      int trigger_player,
10937                                      int trigger_side)
10938 {
10939   boolean change_done = FALSE;
10940   int p;
10941
10942   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10943       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10944     return FALSE;
10945
10946   if (Tile[x][y] == EL_BLOCKED)
10947   {
10948     Blocked2Moving(x, y, &x, &y);
10949     element = Tile[x][y];
10950   }
10951
10952   // check if element has already changed or is about to change after moving
10953   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10954        Tile[x][y] != element) ||
10955
10956       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10957        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10958         ChangePage[x][y] != -1)))
10959     return FALSE;
10960
10961   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10962
10963   for (p = 0; p < element_info[element].num_change_pages; p++)
10964   {
10965     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10966
10967     /* check trigger element for all events where the element that is checked
10968        for changing interacts with a directly adjacent element -- this is
10969        different to element changes that affect other elements to change on the
10970        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10971     boolean check_trigger_element =
10972       (trigger_event == CE_TOUCHING_X ||
10973        trigger_event == CE_HITTING_X ||
10974        trigger_event == CE_HIT_BY_X ||
10975        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10976
10977     if (change->can_change_or_has_action &&
10978         change->has_event[trigger_event] &&
10979         change->trigger_side & trigger_side &&
10980         change->trigger_player & trigger_player &&
10981         (!check_trigger_element ||
10982          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10983     {
10984       change->actual_trigger_element = trigger_element;
10985       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10986       change->actual_trigger_player_bits = trigger_player;
10987       change->actual_trigger_side = trigger_side;
10988       change->actual_trigger_ce_value = CustomValue[x][y];
10989       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10990
10991       // special case: trigger element not at (x,y) position for some events
10992       if (check_trigger_element)
10993       {
10994         static struct
10995         {
10996           int dx, dy;
10997         } move_xy[] =
10998           {
10999             {  0,  0 },
11000             { -1,  0 },
11001             { +1,  0 },
11002             {  0,  0 },
11003             {  0, -1 },
11004             {  0,  0 }, { 0, 0 }, { 0, 0 },
11005             {  0, +1 }
11006           };
11007
11008         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11009         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11010
11011         change->actual_trigger_ce_value = CustomValue[xx][yy];
11012         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11013       }
11014
11015       if (change->can_change && !change_done)
11016       {
11017         ChangeDelay[x][y] = 1;
11018         ChangeEvent[x][y] = trigger_event;
11019
11020         HandleElementChange(x, y, p);
11021
11022         change_done = TRUE;
11023       }
11024       else if (change->has_action)
11025       {
11026         ExecuteCustomElementAction(x, y, element, p);
11027         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11028       }
11029     }
11030   }
11031
11032   RECURSION_LOOP_DETECTION_END();
11033
11034   return change_done;
11035 }
11036
11037 static void PlayPlayerSound(struct PlayerInfo *player)
11038 {
11039   int jx = player->jx, jy = player->jy;
11040   int sound_element = player->artwork_element;
11041   int last_action = player->last_action_waiting;
11042   int action = player->action_waiting;
11043
11044   if (player->is_waiting)
11045   {
11046     if (action != last_action)
11047       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11048     else
11049       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11050   }
11051   else
11052   {
11053     if (action != last_action)
11054       StopSound(element_info[sound_element].sound[last_action]);
11055
11056     if (last_action == ACTION_SLEEPING)
11057       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11058   }
11059 }
11060
11061 static void PlayAllPlayersSound(void)
11062 {
11063   int i;
11064
11065   for (i = 0; i < MAX_PLAYERS; i++)
11066     if (stored_player[i].active)
11067       PlayPlayerSound(&stored_player[i]);
11068 }
11069
11070 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11071 {
11072   boolean last_waiting = player->is_waiting;
11073   int move_dir = player->MovDir;
11074
11075   player->dir_waiting = move_dir;
11076   player->last_action_waiting = player->action_waiting;
11077
11078   if (is_waiting)
11079   {
11080     if (!last_waiting)          // not waiting -> waiting
11081     {
11082       player->is_waiting = TRUE;
11083
11084       player->frame_counter_bored =
11085         FrameCounter +
11086         game.player_boring_delay_fixed +
11087         GetSimpleRandom(game.player_boring_delay_random);
11088       player->frame_counter_sleeping =
11089         FrameCounter +
11090         game.player_sleeping_delay_fixed +
11091         GetSimpleRandom(game.player_sleeping_delay_random);
11092
11093       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11094     }
11095
11096     if (game.player_sleeping_delay_fixed +
11097         game.player_sleeping_delay_random > 0 &&
11098         player->anim_delay_counter == 0 &&
11099         player->post_delay_counter == 0 &&
11100         FrameCounter >= player->frame_counter_sleeping)
11101       player->is_sleeping = TRUE;
11102     else if (game.player_boring_delay_fixed +
11103              game.player_boring_delay_random > 0 &&
11104              FrameCounter >= player->frame_counter_bored)
11105       player->is_bored = TRUE;
11106
11107     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11108                               player->is_bored ? ACTION_BORING :
11109                               ACTION_WAITING);
11110
11111     if (player->is_sleeping && player->use_murphy)
11112     {
11113       // special case for sleeping Murphy when leaning against non-free tile
11114
11115       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11116           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11117            !IS_MOVING(player->jx - 1, player->jy)))
11118         move_dir = MV_LEFT;
11119       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11120                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11121                 !IS_MOVING(player->jx + 1, player->jy)))
11122         move_dir = MV_RIGHT;
11123       else
11124         player->is_sleeping = FALSE;
11125
11126       player->dir_waiting = move_dir;
11127     }
11128
11129     if (player->is_sleeping)
11130     {
11131       if (player->num_special_action_sleeping > 0)
11132       {
11133         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11134         {
11135           int last_special_action = player->special_action_sleeping;
11136           int num_special_action = player->num_special_action_sleeping;
11137           int special_action =
11138             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11139              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11140              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11141              last_special_action + 1 : ACTION_SLEEPING);
11142           int special_graphic =
11143             el_act_dir2img(player->artwork_element, special_action, move_dir);
11144
11145           player->anim_delay_counter =
11146             graphic_info[special_graphic].anim_delay_fixed +
11147             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11148           player->post_delay_counter =
11149             graphic_info[special_graphic].post_delay_fixed +
11150             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11151
11152           player->special_action_sleeping = special_action;
11153         }
11154
11155         if (player->anim_delay_counter > 0)
11156         {
11157           player->action_waiting = player->special_action_sleeping;
11158           player->anim_delay_counter--;
11159         }
11160         else if (player->post_delay_counter > 0)
11161         {
11162           player->post_delay_counter--;
11163         }
11164       }
11165     }
11166     else if (player->is_bored)
11167     {
11168       if (player->num_special_action_bored > 0)
11169       {
11170         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11171         {
11172           int special_action =
11173             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11174           int special_graphic =
11175             el_act_dir2img(player->artwork_element, special_action, move_dir);
11176
11177           player->anim_delay_counter =
11178             graphic_info[special_graphic].anim_delay_fixed +
11179             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11180           player->post_delay_counter =
11181             graphic_info[special_graphic].post_delay_fixed +
11182             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11183
11184           player->special_action_bored = special_action;
11185         }
11186
11187         if (player->anim_delay_counter > 0)
11188         {
11189           player->action_waiting = player->special_action_bored;
11190           player->anim_delay_counter--;
11191         }
11192         else if (player->post_delay_counter > 0)
11193         {
11194           player->post_delay_counter--;
11195         }
11196       }
11197     }
11198   }
11199   else if (last_waiting)        // waiting -> not waiting
11200   {
11201     player->is_waiting = FALSE;
11202     player->is_bored = FALSE;
11203     player->is_sleeping = FALSE;
11204
11205     player->frame_counter_bored = -1;
11206     player->frame_counter_sleeping = -1;
11207
11208     player->anim_delay_counter = 0;
11209     player->post_delay_counter = 0;
11210
11211     player->dir_waiting = player->MovDir;
11212     player->action_waiting = ACTION_DEFAULT;
11213
11214     player->special_action_bored = ACTION_DEFAULT;
11215     player->special_action_sleeping = ACTION_DEFAULT;
11216   }
11217 }
11218
11219 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11220 {
11221   if ((!player->is_moving  && player->was_moving) ||
11222       (player->MovPos == 0 && player->was_moving) ||
11223       (player->is_snapping && !player->was_snapping) ||
11224       (player->is_dropping && !player->was_dropping))
11225   {
11226     if (!CheckSaveEngineSnapshotToList())
11227       return;
11228
11229     player->was_moving = FALSE;
11230     player->was_snapping = TRUE;
11231     player->was_dropping = TRUE;
11232   }
11233   else
11234   {
11235     if (player->is_moving)
11236       player->was_moving = TRUE;
11237
11238     if (!player->is_snapping)
11239       player->was_snapping = FALSE;
11240
11241     if (!player->is_dropping)
11242       player->was_dropping = FALSE;
11243   }
11244
11245   static struct MouseActionInfo mouse_action_last = { 0 };
11246   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11247   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11248
11249   if (new_released)
11250     CheckSaveEngineSnapshotToList();
11251
11252   mouse_action_last = mouse_action;
11253 }
11254
11255 static void CheckSingleStepMode(struct PlayerInfo *player)
11256 {
11257   if (tape.single_step && tape.recording && !tape.pausing)
11258   {
11259     /* as it is called "single step mode", just return to pause mode when the
11260        player stopped moving after one tile (or never starts moving at all) */
11261     if (!player->is_moving &&
11262         !player->is_pushing &&
11263         !player->is_dropping_pressed &&
11264         !player->effective_mouse_action.button)
11265       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11266   }
11267
11268   CheckSaveEngineSnapshot(player);
11269 }
11270
11271 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11272 {
11273   int left      = player_action & JOY_LEFT;
11274   int right     = player_action & JOY_RIGHT;
11275   int up        = player_action & JOY_UP;
11276   int down      = player_action & JOY_DOWN;
11277   int button1   = player_action & JOY_BUTTON_1;
11278   int button2   = player_action & JOY_BUTTON_2;
11279   int dx        = (left ? -1 : right ? 1 : 0);
11280   int dy        = (up   ? -1 : down  ? 1 : 0);
11281
11282   if (!player->active || tape.pausing)
11283     return 0;
11284
11285   if (player_action)
11286   {
11287     if (button1)
11288       SnapField(player, dx, dy);
11289     else
11290     {
11291       if (button2)
11292         DropElement(player);
11293
11294       MovePlayer(player, dx, dy);
11295     }
11296
11297     CheckSingleStepMode(player);
11298
11299     SetPlayerWaiting(player, FALSE);
11300
11301     return player_action;
11302   }
11303   else
11304   {
11305     // no actions for this player (no input at player's configured device)
11306
11307     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11308     SnapField(player, 0, 0);
11309     CheckGravityMovementWhenNotMoving(player);
11310
11311     if (player->MovPos == 0)
11312       SetPlayerWaiting(player, TRUE);
11313
11314     if (player->MovPos == 0)    // needed for tape.playing
11315       player->is_moving = FALSE;
11316
11317     player->is_dropping = FALSE;
11318     player->is_dropping_pressed = FALSE;
11319     player->drop_pressed_delay = 0;
11320
11321     CheckSingleStepMode(player);
11322
11323     return 0;
11324   }
11325 }
11326
11327 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11328                                          byte *tape_action)
11329 {
11330   if (!tape.use_mouse_actions)
11331     return;
11332
11333   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11334   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11335   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11336 }
11337
11338 static void SetTapeActionFromMouseAction(byte *tape_action,
11339                                          struct MouseActionInfo *mouse_action)
11340 {
11341   if (!tape.use_mouse_actions)
11342     return;
11343
11344   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11345   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11346   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11347 }
11348
11349 static void CheckLevelSolved(void)
11350 {
11351   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11352   {
11353     if (game_em.level_solved &&
11354         !game_em.game_over)                             // game won
11355     {
11356       LevelSolved();
11357
11358       game_em.game_over = TRUE;
11359
11360       game.all_players_gone = TRUE;
11361     }
11362
11363     if (game_em.game_over)                              // game lost
11364       game.all_players_gone = TRUE;
11365   }
11366   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11367   {
11368     if (game_sp.level_solved &&
11369         !game_sp.game_over)                             // game won
11370     {
11371       LevelSolved();
11372
11373       game_sp.game_over = TRUE;
11374
11375       game.all_players_gone = TRUE;
11376     }
11377
11378     if (game_sp.game_over)                              // game lost
11379       game.all_players_gone = TRUE;
11380   }
11381   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11382   {
11383     if (game_mm.level_solved &&
11384         !game_mm.game_over)                             // game won
11385     {
11386       LevelSolved();
11387
11388       game_mm.game_over = TRUE;
11389
11390       game.all_players_gone = TRUE;
11391     }
11392
11393     if (game_mm.game_over)                              // game lost
11394       game.all_players_gone = TRUE;
11395   }
11396 }
11397
11398 static void CheckLevelTime(void)
11399 {
11400   int i;
11401
11402   if (TimeFrames >= FRAMES_PER_SECOND)
11403   {
11404     TimeFrames = 0;
11405     TapeTime++;
11406
11407     for (i = 0; i < MAX_PLAYERS; i++)
11408     {
11409       struct PlayerInfo *player = &stored_player[i];
11410
11411       if (SHIELD_ON(player))
11412       {
11413         player->shield_normal_time_left--;
11414
11415         if (player->shield_deadly_time_left > 0)
11416           player->shield_deadly_time_left--;
11417       }
11418     }
11419
11420     if (!game.LevelSolved && !level.use_step_counter)
11421     {
11422       TimePlayed++;
11423
11424       if (TimeLeft > 0)
11425       {
11426         TimeLeft--;
11427
11428         if (TimeLeft <= 10 && setup.time_limit)
11429           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11430
11431         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11432            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11433
11434         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11435
11436         if (!TimeLeft && setup.time_limit)
11437         {
11438           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11439             game_em.lev->killed_out_of_time = TRUE;
11440           else
11441             for (i = 0; i < MAX_PLAYERS; i++)
11442               KillPlayer(&stored_player[i]);
11443         }
11444       }
11445       else if (game.no_time_limit && !game.all_players_gone)
11446       {
11447         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11448       }
11449
11450       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11451     }
11452
11453     if (tape.recording || tape.playing)
11454       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11455   }
11456
11457   if (tape.recording || tape.playing)
11458     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11459
11460   UpdateAndDisplayGameControlValues();
11461 }
11462
11463 void AdvanceFrameAndPlayerCounters(int player_nr)
11464 {
11465   int i;
11466
11467   // advance frame counters (global frame counter and time frame counter)
11468   FrameCounter++;
11469   TimeFrames++;
11470
11471   // advance player counters (counters for move delay, move animation etc.)
11472   for (i = 0; i < MAX_PLAYERS; i++)
11473   {
11474     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11475     int move_delay_value = stored_player[i].move_delay_value;
11476     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11477
11478     if (!advance_player_counters)       // not all players may be affected
11479       continue;
11480
11481     if (move_frames == 0)       // less than one move per game frame
11482     {
11483       int stepsize = TILEX / move_delay_value;
11484       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11485       int count = (stored_player[i].is_moving ?
11486                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11487
11488       if (count % delay == 0)
11489         move_frames = 1;
11490     }
11491
11492     stored_player[i].Frame += move_frames;
11493
11494     if (stored_player[i].MovPos != 0)
11495       stored_player[i].StepFrame += move_frames;
11496
11497     if (stored_player[i].move_delay > 0)
11498       stored_player[i].move_delay--;
11499
11500     // due to bugs in previous versions, counter must count up, not down
11501     if (stored_player[i].push_delay != -1)
11502       stored_player[i].push_delay++;
11503
11504     if (stored_player[i].drop_delay > 0)
11505       stored_player[i].drop_delay--;
11506
11507     if (stored_player[i].is_dropping_pressed)
11508       stored_player[i].drop_pressed_delay++;
11509   }
11510 }
11511
11512 void StartGameActions(boolean init_network_game, boolean record_tape,
11513                       int random_seed)
11514 {
11515   unsigned int new_random_seed = InitRND(random_seed);
11516
11517   if (record_tape)
11518     TapeStartRecording(new_random_seed);
11519
11520   if (init_network_game)
11521   {
11522     SendToServer_LevelFile();
11523     SendToServer_StartPlaying();
11524
11525     return;
11526   }
11527
11528   InitGame();
11529 }
11530
11531 static void GameActionsExt(void)
11532 {
11533 #if 0
11534   static unsigned int game_frame_delay = 0;
11535 #endif
11536   unsigned int game_frame_delay_value;
11537   byte *recorded_player_action;
11538   byte summarized_player_action = 0;
11539   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11540   int i;
11541
11542   // detect endless loops, caused by custom element programming
11543   if (recursion_loop_detected && recursion_loop_depth == 0)
11544   {
11545     char *message = getStringCat3("Internal Error! Element ",
11546                                   EL_NAME(recursion_loop_element),
11547                                   " caused endless loop! Quit the game?");
11548
11549     Warn("element '%s' caused endless loop in game engine",
11550          EL_NAME(recursion_loop_element));
11551
11552     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11553
11554     recursion_loop_detected = FALSE;    // if game should be continued
11555
11556     free(message);
11557
11558     return;
11559   }
11560
11561   if (game.restart_level)
11562     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11563
11564   CheckLevelSolved();
11565
11566   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11567     GameWon();
11568
11569   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11570     TapeStop();
11571
11572   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11573     return;
11574
11575   game_frame_delay_value =
11576     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11577
11578   if (tape.playing && tape.warp_forward && !tape.pausing)
11579     game_frame_delay_value = 0;
11580
11581   SetVideoFrameDelay(game_frame_delay_value);
11582
11583   // (de)activate virtual buttons depending on current game status
11584   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11585   {
11586     if (game.all_players_gone)  // if no players there to be controlled anymore
11587       SetOverlayActive(FALSE);
11588     else if (!tape.playing)     // if game continues after tape stopped playing
11589       SetOverlayActive(TRUE);
11590   }
11591
11592 #if 0
11593 #if 0
11594   // ---------- main game synchronization point ----------
11595
11596   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11597
11598   Debug("game:playing:skip", "skip == %d", skip);
11599
11600 #else
11601   // ---------- main game synchronization point ----------
11602
11603   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11604 #endif
11605 #endif
11606
11607   if (network_playing && !network_player_action_received)
11608   {
11609     // try to get network player actions in time
11610
11611     // last chance to get network player actions without main loop delay
11612     HandleNetworking();
11613
11614     // game was quit by network peer
11615     if (game_status != GAME_MODE_PLAYING)
11616       return;
11617
11618     // check if network player actions still missing and game still running
11619     if (!network_player_action_received && !checkGameEnded())
11620       return;           // failed to get network player actions in time
11621
11622     // do not yet reset "network_player_action_received" (for tape.pausing)
11623   }
11624
11625   if (tape.pausing)
11626     return;
11627
11628   // at this point we know that we really continue executing the game
11629
11630   network_player_action_received = FALSE;
11631
11632   // when playing tape, read previously recorded player input from tape data
11633   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11634
11635   local_player->effective_mouse_action = local_player->mouse_action;
11636
11637   if (recorded_player_action != NULL)
11638     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11639                                  recorded_player_action);
11640
11641   // TapePlayAction() may return NULL when toggling to "pause before death"
11642   if (tape.pausing)
11643     return;
11644
11645   if (tape.set_centered_player)
11646   {
11647     game.centered_player_nr_next = tape.centered_player_nr_next;
11648     game.set_centered_player = TRUE;
11649   }
11650
11651   for (i = 0; i < MAX_PLAYERS; i++)
11652   {
11653     summarized_player_action |= stored_player[i].action;
11654
11655     if (!network_playing && (game.team_mode || tape.playing))
11656       stored_player[i].effective_action = stored_player[i].action;
11657   }
11658
11659   if (network_playing && !checkGameEnded())
11660     SendToServer_MovePlayer(summarized_player_action);
11661
11662   // summarize all actions at local players mapped input device position
11663   // (this allows using different input devices in single player mode)
11664   if (!network.enabled && !game.team_mode)
11665     stored_player[map_player_action[local_player->index_nr]].effective_action =
11666       summarized_player_action;
11667
11668   // summarize all actions at centered player in local team mode
11669   if (tape.recording &&
11670       setup.team_mode && !network.enabled &&
11671       setup.input_on_focus &&
11672       game.centered_player_nr != -1)
11673   {
11674     for (i = 0; i < MAX_PLAYERS; i++)
11675       stored_player[map_player_action[i]].effective_action =
11676         (i == game.centered_player_nr ? summarized_player_action : 0);
11677   }
11678
11679   if (recorded_player_action != NULL)
11680     for (i = 0; i < MAX_PLAYERS; i++)
11681       stored_player[i].effective_action = recorded_player_action[i];
11682
11683   for (i = 0; i < MAX_PLAYERS; i++)
11684   {
11685     tape_action[i] = stored_player[i].effective_action;
11686
11687     /* (this may happen in the RND game engine if a player was not present on
11688        the playfield on level start, but appeared later from a custom element */
11689     if (setup.team_mode &&
11690         tape.recording &&
11691         tape_action[i] &&
11692         !tape.player_participates[i])
11693       tape.player_participates[i] = TRUE;
11694   }
11695
11696   SetTapeActionFromMouseAction(tape_action,
11697                                &local_player->effective_mouse_action);
11698
11699   // only record actions from input devices, but not programmed actions
11700   if (tape.recording)
11701     TapeRecordAction(tape_action);
11702
11703   // remember if game was played (especially after tape stopped playing)
11704   if (!tape.playing && summarized_player_action)
11705     game.GamePlayed = TRUE;
11706
11707 #if USE_NEW_PLAYER_ASSIGNMENTS
11708   // !!! also map player actions in single player mode !!!
11709   // if (game.team_mode)
11710   if (1)
11711   {
11712     byte mapped_action[MAX_PLAYERS];
11713
11714 #if DEBUG_PLAYER_ACTIONS
11715     for (i = 0; i < MAX_PLAYERS; i++)
11716       DebugContinued("", "%d, ", stored_player[i].effective_action);
11717 #endif
11718
11719     for (i = 0; i < MAX_PLAYERS; i++)
11720       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11721
11722     for (i = 0; i < MAX_PLAYERS; i++)
11723       stored_player[i].effective_action = mapped_action[i];
11724
11725 #if DEBUG_PLAYER_ACTIONS
11726     DebugContinued("", "=> ");
11727     for (i = 0; i < MAX_PLAYERS; i++)
11728       DebugContinued("", "%d, ", stored_player[i].effective_action);
11729     DebugContinued("game:playing:player", "\n");
11730 #endif
11731   }
11732 #if DEBUG_PLAYER_ACTIONS
11733   else
11734   {
11735     for (i = 0; i < MAX_PLAYERS; i++)
11736       DebugContinued("", "%d, ", stored_player[i].effective_action);
11737     DebugContinued("game:playing:player", "\n");
11738   }
11739 #endif
11740 #endif
11741
11742   for (i = 0; i < MAX_PLAYERS; i++)
11743   {
11744     // allow engine snapshot in case of changed movement attempt
11745     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11746         (stored_player[i].effective_action & KEY_MOTION))
11747       game.snapshot.changed_action = TRUE;
11748
11749     // allow engine snapshot in case of snapping/dropping attempt
11750     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11751         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11752       game.snapshot.changed_action = TRUE;
11753
11754     game.snapshot.last_action[i] = stored_player[i].effective_action;
11755   }
11756
11757   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11758   {
11759     GameActions_EM_Main();
11760   }
11761   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11762   {
11763     GameActions_SP_Main();
11764   }
11765   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11766   {
11767     GameActions_MM_Main();
11768   }
11769   else
11770   {
11771     GameActions_RND_Main();
11772   }
11773
11774   BlitScreenToBitmap(backbuffer);
11775
11776   CheckLevelSolved();
11777   CheckLevelTime();
11778
11779   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11780
11781   if (global.show_frames_per_second)
11782   {
11783     static unsigned int fps_counter = 0;
11784     static int fps_frames = 0;
11785     unsigned int fps_delay_ms = Counter() - fps_counter;
11786
11787     fps_frames++;
11788
11789     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11790     {
11791       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11792
11793       fps_frames = 0;
11794       fps_counter = Counter();
11795
11796       // always draw FPS to screen after FPS value was updated
11797       redraw_mask |= REDRAW_FPS;
11798     }
11799
11800     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11801     if (GetDrawDeactivationMask() == REDRAW_NONE)
11802       redraw_mask |= REDRAW_FPS;
11803   }
11804 }
11805
11806 static void GameActions_CheckSaveEngineSnapshot(void)
11807 {
11808   if (!game.snapshot.save_snapshot)
11809     return;
11810
11811   // clear flag for saving snapshot _before_ saving snapshot
11812   game.snapshot.save_snapshot = FALSE;
11813
11814   SaveEngineSnapshotToList();
11815 }
11816
11817 void GameActions(void)
11818 {
11819   GameActionsExt();
11820
11821   GameActions_CheckSaveEngineSnapshot();
11822 }
11823
11824 void GameActions_EM_Main(void)
11825 {
11826   byte effective_action[MAX_PLAYERS];
11827   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11828   int i;
11829
11830   for (i = 0; i < MAX_PLAYERS; i++)
11831     effective_action[i] = stored_player[i].effective_action;
11832
11833   GameActions_EM(effective_action, warp_mode);
11834 }
11835
11836 void GameActions_SP_Main(void)
11837 {
11838   byte effective_action[MAX_PLAYERS];
11839   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11840   int i;
11841
11842   for (i = 0; i < MAX_PLAYERS; i++)
11843     effective_action[i] = stored_player[i].effective_action;
11844
11845   GameActions_SP(effective_action, warp_mode);
11846
11847   for (i = 0; i < MAX_PLAYERS; i++)
11848   {
11849     if (stored_player[i].force_dropping)
11850       stored_player[i].action |= KEY_BUTTON_DROP;
11851
11852     stored_player[i].force_dropping = FALSE;
11853   }
11854 }
11855
11856 void GameActions_MM_Main(void)
11857 {
11858   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11859
11860   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11861 }
11862
11863 void GameActions_RND_Main(void)
11864 {
11865   GameActions_RND();
11866 }
11867
11868 void GameActions_RND(void)
11869 {
11870   static struct MouseActionInfo mouse_action_last = { 0 };
11871   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11872   int magic_wall_x = 0, magic_wall_y = 0;
11873   int i, x, y, element, graphic, last_gfx_frame;
11874
11875   InitPlayfieldScanModeVars();
11876
11877   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11878   {
11879     SCAN_PLAYFIELD(x, y)
11880     {
11881       ChangeCount[x][y] = 0;
11882       ChangeEvent[x][y] = -1;
11883     }
11884   }
11885
11886   if (game.set_centered_player)
11887   {
11888     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11889
11890     // switching to "all players" only possible if all players fit to screen
11891     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11892     {
11893       game.centered_player_nr_next = game.centered_player_nr;
11894       game.set_centered_player = FALSE;
11895     }
11896
11897     // do not switch focus to non-existing (or non-active) player
11898     if (game.centered_player_nr_next >= 0 &&
11899         !stored_player[game.centered_player_nr_next].active)
11900     {
11901       game.centered_player_nr_next = game.centered_player_nr;
11902       game.set_centered_player = FALSE;
11903     }
11904   }
11905
11906   if (game.set_centered_player &&
11907       ScreenMovPos == 0)        // screen currently aligned at tile position
11908   {
11909     int sx, sy;
11910
11911     if (game.centered_player_nr_next == -1)
11912     {
11913       setScreenCenteredToAllPlayers(&sx, &sy);
11914     }
11915     else
11916     {
11917       sx = stored_player[game.centered_player_nr_next].jx;
11918       sy = stored_player[game.centered_player_nr_next].jy;
11919     }
11920
11921     game.centered_player_nr = game.centered_player_nr_next;
11922     game.set_centered_player = FALSE;
11923
11924     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11925     DrawGameDoorValues();
11926   }
11927
11928   for (i = 0; i < MAX_PLAYERS; i++)
11929   {
11930     int actual_player_action = stored_player[i].effective_action;
11931
11932 #if 1
11933     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11934        - rnd_equinox_tetrachloride 048
11935        - rnd_equinox_tetrachloride_ii 096
11936        - rnd_emanuel_schmieg 002
11937        - doctor_sloan_ww 001, 020
11938     */
11939     if (stored_player[i].MovPos == 0)
11940       CheckGravityMovement(&stored_player[i]);
11941 #endif
11942
11943     // overwrite programmed action with tape action
11944     if (stored_player[i].programmed_action)
11945       actual_player_action = stored_player[i].programmed_action;
11946
11947     PlayerActions(&stored_player[i], actual_player_action);
11948
11949     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11950   }
11951
11952   ScrollScreen(NULL, SCROLL_GO_ON);
11953
11954   /* for backwards compatibility, the following code emulates a fixed bug that
11955      occured when pushing elements (causing elements that just made their last
11956      pushing step to already (if possible) make their first falling step in the
11957      same game frame, which is bad); this code is also needed to use the famous
11958      "spring push bug" which is used in older levels and might be wanted to be
11959      used also in newer levels, but in this case the buggy pushing code is only
11960      affecting the "spring" element and no other elements */
11961
11962   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11963   {
11964     for (i = 0; i < MAX_PLAYERS; i++)
11965     {
11966       struct PlayerInfo *player = &stored_player[i];
11967       int x = player->jx;
11968       int y = player->jy;
11969
11970       if (player->active && player->is_pushing && player->is_moving &&
11971           IS_MOVING(x, y) &&
11972           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11973            Tile[x][y] == EL_SPRING))
11974       {
11975         ContinueMoving(x, y);
11976
11977         // continue moving after pushing (this is actually a bug)
11978         if (!IS_MOVING(x, y))
11979           Stop[x][y] = FALSE;
11980       }
11981     }
11982   }
11983
11984   SCAN_PLAYFIELD(x, y)
11985   {
11986     Last[x][y] = Tile[x][y];
11987
11988     ChangeCount[x][y] = 0;
11989     ChangeEvent[x][y] = -1;
11990
11991     // this must be handled before main playfield loop
11992     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
11993     {
11994       MovDelay[x][y]--;
11995       if (MovDelay[x][y] <= 0)
11996         RemoveField(x, y);
11997     }
11998
11999     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12000     {
12001       MovDelay[x][y]--;
12002       if (MovDelay[x][y] <= 0)
12003       {
12004         RemoveField(x, y);
12005         TEST_DrawLevelField(x, y);
12006
12007         TestIfElementTouchesCustomElement(x, y);        // for empty space
12008       }
12009     }
12010
12011 #if DEBUG
12012     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12013     {
12014       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12015             x, y);
12016       Debug("game:playing:GameActions_RND", "This should never happen!");
12017
12018       ChangePage[x][y] = -1;
12019     }
12020 #endif
12021
12022     Stop[x][y] = FALSE;
12023     if (WasJustMoving[x][y] > 0)
12024       WasJustMoving[x][y]--;
12025     if (WasJustFalling[x][y] > 0)
12026       WasJustFalling[x][y]--;
12027     if (CheckCollision[x][y] > 0)
12028       CheckCollision[x][y]--;
12029     if (CheckImpact[x][y] > 0)
12030       CheckImpact[x][y]--;
12031
12032     GfxFrame[x][y]++;
12033
12034     /* reset finished pushing action (not done in ContinueMoving() to allow
12035        continuous pushing animation for elements with zero push delay) */
12036     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12037     {
12038       ResetGfxAnimation(x, y);
12039       TEST_DrawLevelField(x, y);
12040     }
12041
12042 #if DEBUG
12043     if (IS_BLOCKED(x, y))
12044     {
12045       int oldx, oldy;
12046
12047       Blocked2Moving(x, y, &oldx, &oldy);
12048       if (!IS_MOVING(oldx, oldy))
12049       {
12050         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12051         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12052         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12053         Debug("game:playing:GameActions_RND", "This should never happen!");
12054       }
12055     }
12056 #endif
12057   }
12058
12059   if (mouse_action.button)
12060   {
12061     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12062
12063     x = mouse_action.lx;
12064     y = mouse_action.ly;
12065     element = Tile[x][y];
12066
12067     if (new_button)
12068     {
12069       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12070       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12071     }
12072
12073     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12074     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12075   }
12076
12077   SCAN_PLAYFIELD(x, y)
12078   {
12079     element = Tile[x][y];
12080     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12081     last_gfx_frame = GfxFrame[x][y];
12082
12083     ResetGfxFrame(x, y);
12084
12085     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12086       DrawLevelGraphicAnimation(x, y, graphic);
12087
12088     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12089         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12090       ResetRandomAnimationValue(x, y);
12091
12092     SetRandomAnimationValue(x, y);
12093
12094     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12095
12096     if (IS_INACTIVE(element))
12097     {
12098       if (IS_ANIMATED(graphic))
12099         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12100
12101       continue;
12102     }
12103
12104     // this may take place after moving, so 'element' may have changed
12105     if (IS_CHANGING(x, y) &&
12106         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12107     {
12108       int page = element_info[element].event_page_nr[CE_DELAY];
12109
12110       HandleElementChange(x, y, page);
12111
12112       element = Tile[x][y];
12113       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12114     }
12115
12116     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12117     {
12118       StartMoving(x, y);
12119
12120       element = Tile[x][y];
12121       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12122
12123       if (IS_ANIMATED(graphic) &&
12124           !IS_MOVING(x, y) &&
12125           !Stop[x][y])
12126         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12127
12128       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12129         TEST_DrawTwinkleOnField(x, y);
12130     }
12131     else if (element == EL_ACID)
12132     {
12133       if (!Stop[x][y])
12134         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12135     }
12136     else if ((element == EL_EXIT_OPEN ||
12137               element == EL_EM_EXIT_OPEN ||
12138               element == EL_SP_EXIT_OPEN ||
12139               element == EL_STEEL_EXIT_OPEN ||
12140               element == EL_EM_STEEL_EXIT_OPEN ||
12141               element == EL_SP_TERMINAL ||
12142               element == EL_SP_TERMINAL_ACTIVE ||
12143               element == EL_EXTRA_TIME ||
12144               element == EL_SHIELD_NORMAL ||
12145               element == EL_SHIELD_DEADLY) &&
12146              IS_ANIMATED(graphic))
12147       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12148     else if (IS_MOVING(x, y))
12149       ContinueMoving(x, y);
12150     else if (IS_ACTIVE_BOMB(element))
12151       CheckDynamite(x, y);
12152     else if (element == EL_AMOEBA_GROWING)
12153       AmoebaGrowing(x, y);
12154     else if (element == EL_AMOEBA_SHRINKING)
12155       AmoebaShrinking(x, y);
12156
12157 #if !USE_NEW_AMOEBA_CODE
12158     else if (IS_AMOEBALIVE(element))
12159       AmoebaReproduce(x, y);
12160 #endif
12161
12162     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12163       Life(x, y);
12164     else if (element == EL_EXIT_CLOSED)
12165       CheckExit(x, y);
12166     else if (element == EL_EM_EXIT_CLOSED)
12167       CheckExitEM(x, y);
12168     else if (element == EL_STEEL_EXIT_CLOSED)
12169       CheckExitSteel(x, y);
12170     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12171       CheckExitSteelEM(x, y);
12172     else if (element == EL_SP_EXIT_CLOSED)
12173       CheckExitSP(x, y);
12174     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12175              element == EL_EXPANDABLE_STEELWALL_GROWING)
12176       MauerWaechst(x, y);
12177     else if (element == EL_EXPANDABLE_WALL ||
12178              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12179              element == EL_EXPANDABLE_WALL_VERTICAL ||
12180              element == EL_EXPANDABLE_WALL_ANY ||
12181              element == EL_BD_EXPANDABLE_WALL)
12182       MauerAbleger(x, y);
12183     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12184              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12185              element == EL_EXPANDABLE_STEELWALL_ANY)
12186       MauerAblegerStahl(x, y);
12187     else if (element == EL_FLAMES)
12188       CheckForDragon(x, y);
12189     else if (element == EL_EXPLOSION)
12190       ; // drawing of correct explosion animation is handled separately
12191     else if (element == EL_ELEMENT_SNAPPING ||
12192              element == EL_DIAGONAL_SHRINKING ||
12193              element == EL_DIAGONAL_GROWING)
12194     {
12195       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12196
12197       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12198     }
12199     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12200       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12201
12202     if (IS_BELT_ACTIVE(element))
12203       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12204
12205     if (game.magic_wall_active)
12206     {
12207       int jx = local_player->jx, jy = local_player->jy;
12208
12209       // play the element sound at the position nearest to the player
12210       if ((element == EL_MAGIC_WALL_FULL ||
12211            element == EL_MAGIC_WALL_ACTIVE ||
12212            element == EL_MAGIC_WALL_EMPTYING ||
12213            element == EL_BD_MAGIC_WALL_FULL ||
12214            element == EL_BD_MAGIC_WALL_ACTIVE ||
12215            element == EL_BD_MAGIC_WALL_EMPTYING ||
12216            element == EL_DC_MAGIC_WALL_FULL ||
12217            element == EL_DC_MAGIC_WALL_ACTIVE ||
12218            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12219           ABS(x - jx) + ABS(y - jy) <
12220           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12221       {
12222         magic_wall_x = x;
12223         magic_wall_y = y;
12224       }
12225     }
12226   }
12227
12228 #if USE_NEW_AMOEBA_CODE
12229   // new experimental amoeba growth stuff
12230   if (!(FrameCounter % 8))
12231   {
12232     static unsigned int random = 1684108901;
12233
12234     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12235     {
12236       x = RND(lev_fieldx);
12237       y = RND(lev_fieldy);
12238       element = Tile[x][y];
12239
12240       if (!IS_PLAYER(x,y) &&
12241           (element == EL_EMPTY ||
12242            CAN_GROW_INTO(element) ||
12243            element == EL_QUICKSAND_EMPTY ||
12244            element == EL_QUICKSAND_FAST_EMPTY ||
12245            element == EL_ACID_SPLASH_LEFT ||
12246            element == EL_ACID_SPLASH_RIGHT))
12247       {
12248         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12249             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12250             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12251             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12252           Tile[x][y] = EL_AMOEBA_DROP;
12253       }
12254
12255       random = random * 129 + 1;
12256     }
12257   }
12258 #endif
12259
12260   game.explosions_delayed = FALSE;
12261
12262   SCAN_PLAYFIELD(x, y)
12263   {
12264     element = Tile[x][y];
12265
12266     if (ExplodeField[x][y])
12267       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12268     else if (element == EL_EXPLOSION)
12269       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12270
12271     ExplodeField[x][y] = EX_TYPE_NONE;
12272   }
12273
12274   game.explosions_delayed = TRUE;
12275
12276   if (game.magic_wall_active)
12277   {
12278     if (!(game.magic_wall_time_left % 4))
12279     {
12280       int element = Tile[magic_wall_x][magic_wall_y];
12281
12282       if (element == EL_BD_MAGIC_WALL_FULL ||
12283           element == EL_BD_MAGIC_WALL_ACTIVE ||
12284           element == EL_BD_MAGIC_WALL_EMPTYING)
12285         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12286       else if (element == EL_DC_MAGIC_WALL_FULL ||
12287                element == EL_DC_MAGIC_WALL_ACTIVE ||
12288                element == EL_DC_MAGIC_WALL_EMPTYING)
12289         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12290       else
12291         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12292     }
12293
12294     if (game.magic_wall_time_left > 0)
12295     {
12296       game.magic_wall_time_left--;
12297
12298       if (!game.magic_wall_time_left)
12299       {
12300         SCAN_PLAYFIELD(x, y)
12301         {
12302           element = Tile[x][y];
12303
12304           if (element == EL_MAGIC_WALL_ACTIVE ||
12305               element == EL_MAGIC_WALL_FULL)
12306           {
12307             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12308             TEST_DrawLevelField(x, y);
12309           }
12310           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12311                    element == EL_BD_MAGIC_WALL_FULL)
12312           {
12313             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12314             TEST_DrawLevelField(x, y);
12315           }
12316           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12317                    element == EL_DC_MAGIC_WALL_FULL)
12318           {
12319             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12320             TEST_DrawLevelField(x, y);
12321           }
12322         }
12323
12324         game.magic_wall_active = FALSE;
12325       }
12326     }
12327   }
12328
12329   if (game.light_time_left > 0)
12330   {
12331     game.light_time_left--;
12332
12333     if (game.light_time_left == 0)
12334       RedrawAllLightSwitchesAndInvisibleElements();
12335   }
12336
12337   if (game.timegate_time_left > 0)
12338   {
12339     game.timegate_time_left--;
12340
12341     if (game.timegate_time_left == 0)
12342       CloseAllOpenTimegates();
12343   }
12344
12345   if (game.lenses_time_left > 0)
12346   {
12347     game.lenses_time_left--;
12348
12349     if (game.lenses_time_left == 0)
12350       RedrawAllInvisibleElementsForLenses();
12351   }
12352
12353   if (game.magnify_time_left > 0)
12354   {
12355     game.magnify_time_left--;
12356
12357     if (game.magnify_time_left == 0)
12358       RedrawAllInvisibleElementsForMagnifier();
12359   }
12360
12361   for (i = 0; i < MAX_PLAYERS; i++)
12362   {
12363     struct PlayerInfo *player = &stored_player[i];
12364
12365     if (SHIELD_ON(player))
12366     {
12367       if (player->shield_deadly_time_left)
12368         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12369       else if (player->shield_normal_time_left)
12370         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12371     }
12372   }
12373
12374 #if USE_DELAYED_GFX_REDRAW
12375   SCAN_PLAYFIELD(x, y)
12376   {
12377     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12378     {
12379       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12380          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12381
12382       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12383         DrawLevelField(x, y);
12384
12385       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12386         DrawLevelFieldCrumbled(x, y);
12387
12388       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12389         DrawLevelFieldCrumbledNeighbours(x, y);
12390
12391       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12392         DrawTwinkleOnField(x, y);
12393     }
12394
12395     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12396   }
12397 #endif
12398
12399   DrawAllPlayers();
12400   PlayAllPlayersSound();
12401
12402   for (i = 0; i < MAX_PLAYERS; i++)
12403   {
12404     struct PlayerInfo *player = &stored_player[i];
12405
12406     if (player->show_envelope != 0 && (!player->active ||
12407                                        player->MovPos == 0))
12408     {
12409       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12410
12411       player->show_envelope = 0;
12412     }
12413   }
12414
12415   // use random number generator in every frame to make it less predictable
12416   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12417     RND(1);
12418
12419   mouse_action_last = mouse_action;
12420 }
12421
12422 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12423 {
12424   int min_x = x, min_y = y, max_x = x, max_y = y;
12425   int i;
12426
12427   for (i = 0; i < MAX_PLAYERS; i++)
12428   {
12429     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12430
12431     if (!stored_player[i].active || &stored_player[i] == player)
12432       continue;
12433
12434     min_x = MIN(min_x, jx);
12435     min_y = MIN(min_y, jy);
12436     max_x = MAX(max_x, jx);
12437     max_y = MAX(max_y, jy);
12438   }
12439
12440   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12441 }
12442
12443 static boolean AllPlayersInVisibleScreen(void)
12444 {
12445   int i;
12446
12447   for (i = 0; i < MAX_PLAYERS; i++)
12448   {
12449     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12450
12451     if (!stored_player[i].active)
12452       continue;
12453
12454     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12455       return FALSE;
12456   }
12457
12458   return TRUE;
12459 }
12460
12461 void ScrollLevel(int dx, int dy)
12462 {
12463   int scroll_offset = 2 * TILEX_VAR;
12464   int x, y;
12465
12466   BlitBitmap(drawto_field, drawto_field,
12467              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12468              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12469              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12470              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12471              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12472              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12473
12474   if (dx != 0)
12475   {
12476     x = (dx == 1 ? BX1 : BX2);
12477     for (y = BY1; y <= BY2; y++)
12478       DrawScreenField(x, y);
12479   }
12480
12481   if (dy != 0)
12482   {
12483     y = (dy == 1 ? BY1 : BY2);
12484     for (x = BX1; x <= BX2; x++)
12485       DrawScreenField(x, y);
12486   }
12487
12488   redraw_mask |= REDRAW_FIELD;
12489 }
12490
12491 static boolean canFallDown(struct PlayerInfo *player)
12492 {
12493   int jx = player->jx, jy = player->jy;
12494
12495   return (IN_LEV_FIELD(jx, jy + 1) &&
12496           (IS_FREE(jx, jy + 1) ||
12497            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12498           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12499           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12500 }
12501
12502 static boolean canPassField(int x, int y, int move_dir)
12503 {
12504   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12505   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12506   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12507   int nextx = x + dx;
12508   int nexty = y + dy;
12509   int element = Tile[x][y];
12510
12511   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12512           !CAN_MOVE(element) &&
12513           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12514           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12515           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12516 }
12517
12518 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12519 {
12520   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12521   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12522   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12523   int newx = x + dx;
12524   int newy = y + dy;
12525
12526   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12527           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12528           (IS_DIGGABLE(Tile[newx][newy]) ||
12529            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12530            canPassField(newx, newy, move_dir)));
12531 }
12532
12533 static void CheckGravityMovement(struct PlayerInfo *player)
12534 {
12535   if (player->gravity && !player->programmed_action)
12536   {
12537     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12538     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12539     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12540     int jx = player->jx, jy = player->jy;
12541     boolean player_is_moving_to_valid_field =
12542       (!player_is_snapping &&
12543        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12544         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12545     boolean player_can_fall_down = canFallDown(player);
12546
12547     if (player_can_fall_down &&
12548         !player_is_moving_to_valid_field)
12549       player->programmed_action = MV_DOWN;
12550   }
12551 }
12552
12553 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12554 {
12555   return CheckGravityMovement(player);
12556
12557   if (player->gravity && !player->programmed_action)
12558   {
12559     int jx = player->jx, jy = player->jy;
12560     boolean field_under_player_is_free =
12561       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12562     boolean player_is_standing_on_valid_field =
12563       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12564        (IS_WALKABLE(Tile[jx][jy]) &&
12565         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12566
12567     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12568       player->programmed_action = MV_DOWN;
12569   }
12570 }
12571
12572 /*
12573   MovePlayerOneStep()
12574   -----------------------------------------------------------------------------
12575   dx, dy:               direction (non-diagonal) to try to move the player to
12576   real_dx, real_dy:     direction as read from input device (can be diagonal)
12577 */
12578
12579 boolean MovePlayerOneStep(struct PlayerInfo *player,
12580                           int dx, int dy, int real_dx, int real_dy)
12581 {
12582   int jx = player->jx, jy = player->jy;
12583   int new_jx = jx + dx, new_jy = jy + dy;
12584   int can_move;
12585   boolean player_can_move = !player->cannot_move;
12586
12587   if (!player->active || (!dx && !dy))
12588     return MP_NO_ACTION;
12589
12590   player->MovDir = (dx < 0 ? MV_LEFT :
12591                     dx > 0 ? MV_RIGHT :
12592                     dy < 0 ? MV_UP :
12593                     dy > 0 ? MV_DOWN :  MV_NONE);
12594
12595   if (!IN_LEV_FIELD(new_jx, new_jy))
12596     return MP_NO_ACTION;
12597
12598   if (!player_can_move)
12599   {
12600     if (player->MovPos == 0)
12601     {
12602       player->is_moving = FALSE;
12603       player->is_digging = FALSE;
12604       player->is_collecting = FALSE;
12605       player->is_snapping = FALSE;
12606       player->is_pushing = FALSE;
12607     }
12608   }
12609
12610   if (!network.enabled && game.centered_player_nr == -1 &&
12611       !AllPlayersInSight(player, new_jx, new_jy))
12612     return MP_NO_ACTION;
12613
12614   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12615   if (can_move != MP_MOVING)
12616     return can_move;
12617
12618   // check if DigField() has caused relocation of the player
12619   if (player->jx != jx || player->jy != jy)
12620     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12621
12622   StorePlayer[jx][jy] = 0;
12623   player->last_jx = jx;
12624   player->last_jy = jy;
12625   player->jx = new_jx;
12626   player->jy = new_jy;
12627   StorePlayer[new_jx][new_jy] = player->element_nr;
12628
12629   if (player->move_delay_value_next != -1)
12630   {
12631     player->move_delay_value = player->move_delay_value_next;
12632     player->move_delay_value_next = -1;
12633   }
12634
12635   player->MovPos =
12636     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12637
12638   player->step_counter++;
12639
12640   PlayerVisit[jx][jy] = FrameCounter;
12641
12642   player->is_moving = TRUE;
12643
12644 #if 1
12645   // should better be called in MovePlayer(), but this breaks some tapes
12646   ScrollPlayer(player, SCROLL_INIT);
12647 #endif
12648
12649   return MP_MOVING;
12650 }
12651
12652 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12653 {
12654   int jx = player->jx, jy = player->jy;
12655   int old_jx = jx, old_jy = jy;
12656   int moved = MP_NO_ACTION;
12657
12658   if (!player->active)
12659     return FALSE;
12660
12661   if (!dx && !dy)
12662   {
12663     if (player->MovPos == 0)
12664     {
12665       player->is_moving = FALSE;
12666       player->is_digging = FALSE;
12667       player->is_collecting = FALSE;
12668       player->is_snapping = FALSE;
12669       player->is_pushing = FALSE;
12670     }
12671
12672     return FALSE;
12673   }
12674
12675   if (player->move_delay > 0)
12676     return FALSE;
12677
12678   player->move_delay = -1;              // set to "uninitialized" value
12679
12680   // store if player is automatically moved to next field
12681   player->is_auto_moving = (player->programmed_action != MV_NONE);
12682
12683   // remove the last programmed player action
12684   player->programmed_action = 0;
12685
12686   if (player->MovPos)
12687   {
12688     // should only happen if pre-1.2 tape recordings are played
12689     // this is only for backward compatibility
12690
12691     int original_move_delay_value = player->move_delay_value;
12692
12693 #if DEBUG
12694     Debug("game:playing:MovePlayer",
12695           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12696           tape.counter);
12697 #endif
12698
12699     // scroll remaining steps with finest movement resolution
12700     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12701
12702     while (player->MovPos)
12703     {
12704       ScrollPlayer(player, SCROLL_GO_ON);
12705       ScrollScreen(NULL, SCROLL_GO_ON);
12706
12707       AdvanceFrameAndPlayerCounters(player->index_nr);
12708
12709       DrawAllPlayers();
12710       BackToFront_WithFrameDelay(0);
12711     }
12712
12713     player->move_delay_value = original_move_delay_value;
12714   }
12715
12716   player->is_active = FALSE;
12717
12718   if (player->last_move_dir & MV_HORIZONTAL)
12719   {
12720     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12721       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12722   }
12723   else
12724   {
12725     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12726       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12727   }
12728
12729   if (!moved && !player->is_active)
12730   {
12731     player->is_moving = FALSE;
12732     player->is_digging = FALSE;
12733     player->is_collecting = FALSE;
12734     player->is_snapping = FALSE;
12735     player->is_pushing = FALSE;
12736   }
12737
12738   jx = player->jx;
12739   jy = player->jy;
12740
12741   if (moved & MP_MOVING && !ScreenMovPos &&
12742       (player->index_nr == game.centered_player_nr ||
12743        game.centered_player_nr == -1))
12744   {
12745     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12746
12747     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12748     {
12749       // actual player has left the screen -- scroll in that direction
12750       if (jx != old_jx)         // player has moved horizontally
12751         scroll_x += (jx - old_jx);
12752       else                      // player has moved vertically
12753         scroll_y += (jy - old_jy);
12754     }
12755     else
12756     {
12757       int offset_raw = game.scroll_delay_value;
12758
12759       if (jx != old_jx)         // player has moved horizontally
12760       {
12761         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12762         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12763         int new_scroll_x = jx - MIDPOSX + offset_x;
12764
12765         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12766             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12767           scroll_x = new_scroll_x;
12768
12769         // don't scroll over playfield boundaries
12770         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12771
12772         // don't scroll more than one field at a time
12773         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12774
12775         // don't scroll against the player's moving direction
12776         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12777             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12778           scroll_x = old_scroll_x;
12779       }
12780       else                      // player has moved vertically
12781       {
12782         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12783         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12784         int new_scroll_y = jy - MIDPOSY + offset_y;
12785
12786         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12787             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12788           scroll_y = new_scroll_y;
12789
12790         // don't scroll over playfield boundaries
12791         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12792
12793         // don't scroll more than one field at a time
12794         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12795
12796         // don't scroll against the player's moving direction
12797         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12798             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12799           scroll_y = old_scroll_y;
12800       }
12801     }
12802
12803     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12804     {
12805       if (!network.enabled && game.centered_player_nr == -1 &&
12806           !AllPlayersInVisibleScreen())
12807       {
12808         scroll_x = old_scroll_x;
12809         scroll_y = old_scroll_y;
12810       }
12811       else
12812       {
12813         ScrollScreen(player, SCROLL_INIT);
12814         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12815       }
12816     }
12817   }
12818
12819   player->StepFrame = 0;
12820
12821   if (moved & MP_MOVING)
12822   {
12823     if (old_jx != jx && old_jy == jy)
12824       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12825     else if (old_jx == jx && old_jy != jy)
12826       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12827
12828     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12829
12830     player->last_move_dir = player->MovDir;
12831     player->is_moving = TRUE;
12832     player->is_snapping = FALSE;
12833     player->is_switching = FALSE;
12834     player->is_dropping = FALSE;
12835     player->is_dropping_pressed = FALSE;
12836     player->drop_pressed_delay = 0;
12837
12838 #if 0
12839     // should better be called here than above, but this breaks some tapes
12840     ScrollPlayer(player, SCROLL_INIT);
12841 #endif
12842   }
12843   else
12844   {
12845     CheckGravityMovementWhenNotMoving(player);
12846
12847     player->is_moving = FALSE;
12848
12849     /* at this point, the player is allowed to move, but cannot move right now
12850        (e.g. because of something blocking the way) -- ensure that the player
12851        is also allowed to move in the next frame (in old versions before 3.1.1,
12852        the player was forced to wait again for eight frames before next try) */
12853
12854     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12855       player->move_delay = 0;   // allow direct movement in the next frame
12856   }
12857
12858   if (player->move_delay == -1)         // not yet initialized by DigField()
12859     player->move_delay = player->move_delay_value;
12860
12861   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12862   {
12863     TestIfPlayerTouchesBadThing(jx, jy);
12864     TestIfPlayerTouchesCustomElement(jx, jy);
12865   }
12866
12867   if (!player->active)
12868     RemovePlayer(player);
12869
12870   return moved;
12871 }
12872
12873 void ScrollPlayer(struct PlayerInfo *player, int mode)
12874 {
12875   int jx = player->jx, jy = player->jy;
12876   int last_jx = player->last_jx, last_jy = player->last_jy;
12877   int move_stepsize = TILEX / player->move_delay_value;
12878
12879   if (!player->active)
12880     return;
12881
12882   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12883     return;
12884
12885   if (mode == SCROLL_INIT)
12886   {
12887     player->actual_frame_counter = FrameCounter;
12888     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12889
12890     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12891         Tile[last_jx][last_jy] == EL_EMPTY)
12892     {
12893       int last_field_block_delay = 0;   // start with no blocking at all
12894       int block_delay_adjustment = player->block_delay_adjustment;
12895
12896       // if player blocks last field, add delay for exactly one move
12897       if (player->block_last_field)
12898       {
12899         last_field_block_delay += player->move_delay_value;
12900
12901         // when blocking enabled, prevent moving up despite gravity
12902         if (player->gravity && player->MovDir == MV_UP)
12903           block_delay_adjustment = -1;
12904       }
12905
12906       // add block delay adjustment (also possible when not blocking)
12907       last_field_block_delay += block_delay_adjustment;
12908
12909       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12910       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12911     }
12912
12913     if (player->MovPos != 0)    // player has not yet reached destination
12914       return;
12915   }
12916   else if (!FrameReached(&player->actual_frame_counter, 1))
12917     return;
12918
12919   if (player->MovPos != 0)
12920   {
12921     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12922     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12923
12924     // before DrawPlayer() to draw correct player graphic for this case
12925     if (player->MovPos == 0)
12926       CheckGravityMovement(player);
12927   }
12928
12929   if (player->MovPos == 0)      // player reached destination field
12930   {
12931     if (player->move_delay_reset_counter > 0)
12932     {
12933       player->move_delay_reset_counter--;
12934
12935       if (player->move_delay_reset_counter == 0)
12936       {
12937         // continue with normal speed after quickly moving through gate
12938         HALVE_PLAYER_SPEED(player);
12939
12940         // be able to make the next move without delay
12941         player->move_delay = 0;
12942       }
12943     }
12944
12945     player->last_jx = jx;
12946     player->last_jy = jy;
12947
12948     if (Tile[jx][jy] == EL_EXIT_OPEN ||
12949         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
12950         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
12951         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
12952         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12953         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12954         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
12955         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12956     {
12957       ExitPlayer(player);
12958
12959       if (game.players_still_needed == 0 &&
12960           (game.friends_still_needed == 0 ||
12961            IS_SP_ELEMENT(Tile[jx][jy])))
12962         LevelSolved();
12963     }
12964
12965     // this breaks one level: "machine", level 000
12966     {
12967       int move_direction = player->MovDir;
12968       int enter_side = MV_DIR_OPPOSITE(move_direction);
12969       int leave_side = move_direction;
12970       int old_jx = last_jx;
12971       int old_jy = last_jy;
12972       int old_element = Tile[old_jx][old_jy];
12973       int new_element = Tile[jx][jy];
12974
12975       if (IS_CUSTOM_ELEMENT(old_element))
12976         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12977                                    CE_LEFT_BY_PLAYER,
12978                                    player->index_bit, leave_side);
12979
12980       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12981                                           CE_PLAYER_LEAVES_X,
12982                                           player->index_bit, leave_side);
12983
12984       if (IS_CUSTOM_ELEMENT(new_element))
12985         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12986                                    player->index_bit, enter_side);
12987
12988       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12989                                           CE_PLAYER_ENTERS_X,
12990                                           player->index_bit, enter_side);
12991
12992       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12993                                         CE_MOVE_OF_X, move_direction);
12994     }
12995
12996     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12997     {
12998       TestIfPlayerTouchesBadThing(jx, jy);
12999       TestIfPlayerTouchesCustomElement(jx, jy);
13000
13001       /* needed because pushed element has not yet reached its destination,
13002          so it would trigger a change event at its previous field location */
13003       if (!player->is_pushing)
13004         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13005
13006       if (!player->active)
13007         RemovePlayer(player);
13008     }
13009
13010     if (!game.LevelSolved && level.use_step_counter)
13011     {
13012       int i;
13013
13014       TimePlayed++;
13015
13016       if (TimeLeft > 0)
13017       {
13018         TimeLeft--;
13019
13020         if (TimeLeft <= 10 && setup.time_limit)
13021           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13022
13023         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13024
13025         DisplayGameControlValues();
13026
13027         if (!TimeLeft && setup.time_limit)
13028           for (i = 0; i < MAX_PLAYERS; i++)
13029             KillPlayer(&stored_player[i]);
13030       }
13031       else if (game.no_time_limit && !game.all_players_gone)
13032       {
13033         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13034
13035         DisplayGameControlValues();
13036       }
13037     }
13038
13039     if (tape.single_step && tape.recording && !tape.pausing &&
13040         !player->programmed_action)
13041       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13042
13043     if (!player->programmed_action)
13044       CheckSaveEngineSnapshot(player);
13045   }
13046 }
13047
13048 void ScrollScreen(struct PlayerInfo *player, int mode)
13049 {
13050   static unsigned int screen_frame_counter = 0;
13051
13052   if (mode == SCROLL_INIT)
13053   {
13054     // set scrolling step size according to actual player's moving speed
13055     ScrollStepSize = TILEX / player->move_delay_value;
13056
13057     screen_frame_counter = FrameCounter;
13058     ScreenMovDir = player->MovDir;
13059     ScreenMovPos = player->MovPos;
13060     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13061     return;
13062   }
13063   else if (!FrameReached(&screen_frame_counter, 1))
13064     return;
13065
13066   if (ScreenMovPos)
13067   {
13068     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13069     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13070     redraw_mask |= REDRAW_FIELD;
13071   }
13072   else
13073     ScreenMovDir = MV_NONE;
13074 }
13075
13076 void TestIfPlayerTouchesCustomElement(int x, int y)
13077 {
13078   static int xy[4][2] =
13079   {
13080     { 0, -1 },
13081     { -1, 0 },
13082     { +1, 0 },
13083     { 0, +1 }
13084   };
13085   static int trigger_sides[4][2] =
13086   {
13087     // center side       border side
13088     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13089     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13090     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13091     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13092   };
13093   static int touch_dir[4] =
13094   {
13095     MV_LEFT | MV_RIGHT,
13096     MV_UP   | MV_DOWN,
13097     MV_UP   | MV_DOWN,
13098     MV_LEFT | MV_RIGHT
13099   };
13100   int center_element = Tile[x][y];      // should always be non-moving!
13101   int i;
13102
13103   for (i = 0; i < NUM_DIRECTIONS; i++)
13104   {
13105     int xx = x + xy[i][0];
13106     int yy = y + xy[i][1];
13107     int center_side = trigger_sides[i][0];
13108     int border_side = trigger_sides[i][1];
13109     int border_element;
13110
13111     if (!IN_LEV_FIELD(xx, yy))
13112       continue;
13113
13114     if (IS_PLAYER(x, y))                // player found at center element
13115     {
13116       struct PlayerInfo *player = PLAYERINFO(x, y);
13117
13118       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13119         border_element = Tile[xx][yy];          // may be moving!
13120       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13121         border_element = Tile[xx][yy];
13122       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13123         border_element = MovingOrBlocked2Element(xx, yy);
13124       else
13125         continue;               // center and border element do not touch
13126
13127       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13128                                  player->index_bit, border_side);
13129       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13130                                           CE_PLAYER_TOUCHES_X,
13131                                           player->index_bit, border_side);
13132
13133       {
13134         /* use player element that is initially defined in the level playfield,
13135            not the player element that corresponds to the runtime player number
13136            (example: a level that contains EL_PLAYER_3 as the only player would
13137            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13138         int player_element = PLAYERINFO(x, y)->initial_element;
13139
13140         CheckElementChangeBySide(xx, yy, border_element, player_element,
13141                                  CE_TOUCHING_X, border_side);
13142       }
13143     }
13144     else if (IS_PLAYER(xx, yy))         // player found at border element
13145     {
13146       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13147
13148       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13149       {
13150         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13151           continue;             // center and border element do not touch
13152       }
13153
13154       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13155                                  player->index_bit, center_side);
13156       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13157                                           CE_PLAYER_TOUCHES_X,
13158                                           player->index_bit, center_side);
13159
13160       {
13161         /* use player element that is initially defined in the level playfield,
13162            not the player element that corresponds to the runtime player number
13163            (example: a level that contains EL_PLAYER_3 as the only player would
13164            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13165         int player_element = PLAYERINFO(xx, yy)->initial_element;
13166
13167         CheckElementChangeBySide(x, y, center_element, player_element,
13168                                  CE_TOUCHING_X, center_side);
13169       }
13170
13171       break;
13172     }
13173   }
13174 }
13175
13176 void TestIfElementTouchesCustomElement(int x, int y)
13177 {
13178   static int xy[4][2] =
13179   {
13180     { 0, -1 },
13181     { -1, 0 },
13182     { +1, 0 },
13183     { 0, +1 }
13184   };
13185   static int trigger_sides[4][2] =
13186   {
13187     // center side      border side
13188     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13189     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13190     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13191     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13192   };
13193   static int touch_dir[4] =
13194   {
13195     MV_LEFT | MV_RIGHT,
13196     MV_UP   | MV_DOWN,
13197     MV_UP   | MV_DOWN,
13198     MV_LEFT | MV_RIGHT
13199   };
13200   boolean change_center_element = FALSE;
13201   int center_element = Tile[x][y];      // should always be non-moving!
13202   int border_element_old[NUM_DIRECTIONS];
13203   int i;
13204
13205   for (i = 0; i < NUM_DIRECTIONS; i++)
13206   {
13207     int xx = x + xy[i][0];
13208     int yy = y + xy[i][1];
13209     int border_element;
13210
13211     border_element_old[i] = -1;
13212
13213     if (!IN_LEV_FIELD(xx, yy))
13214       continue;
13215
13216     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13217       border_element = Tile[xx][yy];    // may be moving!
13218     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13219       border_element = Tile[xx][yy];
13220     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13221       border_element = MovingOrBlocked2Element(xx, yy);
13222     else
13223       continue;                 // center and border element do not touch
13224
13225     border_element_old[i] = border_element;
13226   }
13227
13228   for (i = 0; i < NUM_DIRECTIONS; i++)
13229   {
13230     int xx = x + xy[i][0];
13231     int yy = y + xy[i][1];
13232     int center_side = trigger_sides[i][0];
13233     int border_element = border_element_old[i];
13234
13235     if (border_element == -1)
13236       continue;
13237
13238     // check for change of border element
13239     CheckElementChangeBySide(xx, yy, border_element, center_element,
13240                              CE_TOUCHING_X, center_side);
13241
13242     // (center element cannot be player, so we dont have to check this here)
13243   }
13244
13245   for (i = 0; i < NUM_DIRECTIONS; i++)
13246   {
13247     int xx = x + xy[i][0];
13248     int yy = y + xy[i][1];
13249     int border_side = trigger_sides[i][1];
13250     int border_element = border_element_old[i];
13251
13252     if (border_element == -1)
13253       continue;
13254
13255     // check for change of center element (but change it only once)
13256     if (!change_center_element)
13257       change_center_element =
13258         CheckElementChangeBySide(x, y, center_element, border_element,
13259                                  CE_TOUCHING_X, border_side);
13260
13261     if (IS_PLAYER(xx, yy))
13262     {
13263       /* use player element that is initially defined in the level playfield,
13264          not the player element that corresponds to the runtime player number
13265          (example: a level that contains EL_PLAYER_3 as the only player would
13266          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13267       int player_element = PLAYERINFO(xx, yy)->initial_element;
13268
13269       CheckElementChangeBySide(x, y, center_element, player_element,
13270                                CE_TOUCHING_X, border_side);
13271     }
13272   }
13273 }
13274
13275 void TestIfElementHitsCustomElement(int x, int y, int direction)
13276 {
13277   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13278   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13279   int hitx = x + dx, hity = y + dy;
13280   int hitting_element = Tile[x][y];
13281   int touched_element;
13282
13283   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13284     return;
13285
13286   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13287                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13288
13289   if (IN_LEV_FIELD(hitx, hity))
13290   {
13291     int opposite_direction = MV_DIR_OPPOSITE(direction);
13292     int hitting_side = direction;
13293     int touched_side = opposite_direction;
13294     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13295                           MovDir[hitx][hity] != direction ||
13296                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13297
13298     object_hit = TRUE;
13299
13300     if (object_hit)
13301     {
13302       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13303                                CE_HITTING_X, touched_side);
13304
13305       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13306                                CE_HIT_BY_X, hitting_side);
13307
13308       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13309                                CE_HIT_BY_SOMETHING, opposite_direction);
13310
13311       if (IS_PLAYER(hitx, hity))
13312       {
13313         /* use player element that is initially defined in the level playfield,
13314            not the player element that corresponds to the runtime player number
13315            (example: a level that contains EL_PLAYER_3 as the only player would
13316            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13317         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13318
13319         CheckElementChangeBySide(x, y, hitting_element, player_element,
13320                                  CE_HITTING_X, touched_side);
13321       }
13322     }
13323   }
13324
13325   // "hitting something" is also true when hitting the playfield border
13326   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13327                            CE_HITTING_SOMETHING, direction);
13328 }
13329
13330 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13331 {
13332   int i, kill_x = -1, kill_y = -1;
13333
13334   int bad_element = -1;
13335   static int test_xy[4][2] =
13336   {
13337     { 0, -1 },
13338     { -1, 0 },
13339     { +1, 0 },
13340     { 0, +1 }
13341   };
13342   static int test_dir[4] =
13343   {
13344     MV_UP,
13345     MV_LEFT,
13346     MV_RIGHT,
13347     MV_DOWN
13348   };
13349
13350   for (i = 0; i < NUM_DIRECTIONS; i++)
13351   {
13352     int test_x, test_y, test_move_dir, test_element;
13353
13354     test_x = good_x + test_xy[i][0];
13355     test_y = good_y + test_xy[i][1];
13356
13357     if (!IN_LEV_FIELD(test_x, test_y))
13358       continue;
13359
13360     test_move_dir =
13361       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13362
13363     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13364
13365     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13366        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13367     */
13368     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13369         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13370     {
13371       kill_x = test_x;
13372       kill_y = test_y;
13373       bad_element = test_element;
13374
13375       break;
13376     }
13377   }
13378
13379   if (kill_x != -1 || kill_y != -1)
13380   {
13381     if (IS_PLAYER(good_x, good_y))
13382     {
13383       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13384
13385       if (player->shield_deadly_time_left > 0 &&
13386           !IS_INDESTRUCTIBLE(bad_element))
13387         Bang(kill_x, kill_y);
13388       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13389         KillPlayer(player);
13390     }
13391     else
13392       Bang(good_x, good_y);
13393   }
13394 }
13395
13396 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13397 {
13398   int i, kill_x = -1, kill_y = -1;
13399   int bad_element = Tile[bad_x][bad_y];
13400   static int test_xy[4][2] =
13401   {
13402     { 0, -1 },
13403     { -1, 0 },
13404     { +1, 0 },
13405     { 0, +1 }
13406   };
13407   static int touch_dir[4] =
13408   {
13409     MV_LEFT | MV_RIGHT,
13410     MV_UP   | MV_DOWN,
13411     MV_UP   | MV_DOWN,
13412     MV_LEFT | MV_RIGHT
13413   };
13414   static int test_dir[4] =
13415   {
13416     MV_UP,
13417     MV_LEFT,
13418     MV_RIGHT,
13419     MV_DOWN
13420   };
13421
13422   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13423     return;
13424
13425   for (i = 0; i < NUM_DIRECTIONS; i++)
13426   {
13427     int test_x, test_y, test_move_dir, test_element;
13428
13429     test_x = bad_x + test_xy[i][0];
13430     test_y = bad_y + test_xy[i][1];
13431
13432     if (!IN_LEV_FIELD(test_x, test_y))
13433       continue;
13434
13435     test_move_dir =
13436       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13437
13438     test_element = Tile[test_x][test_y];
13439
13440     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13441        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13442     */
13443     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13444         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13445     {
13446       // good thing is player or penguin that does not move away
13447       if (IS_PLAYER(test_x, test_y))
13448       {
13449         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13450
13451         if (bad_element == EL_ROBOT && player->is_moving)
13452           continue;     // robot does not kill player if he is moving
13453
13454         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13455         {
13456           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13457             continue;           // center and border element do not touch
13458         }
13459
13460         kill_x = test_x;
13461         kill_y = test_y;
13462
13463         break;
13464       }
13465       else if (test_element == EL_PENGUIN)
13466       {
13467         kill_x = test_x;
13468         kill_y = test_y;
13469
13470         break;
13471       }
13472     }
13473   }
13474
13475   if (kill_x != -1 || kill_y != -1)
13476   {
13477     if (IS_PLAYER(kill_x, kill_y))
13478     {
13479       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13480
13481       if (player->shield_deadly_time_left > 0 &&
13482           !IS_INDESTRUCTIBLE(bad_element))
13483         Bang(bad_x, bad_y);
13484       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13485         KillPlayer(player);
13486     }
13487     else
13488       Bang(kill_x, kill_y);
13489   }
13490 }
13491
13492 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13493 {
13494   int bad_element = Tile[bad_x][bad_y];
13495   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13496   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13497   int test_x = bad_x + dx, test_y = bad_y + dy;
13498   int test_move_dir, test_element;
13499   int kill_x = -1, kill_y = -1;
13500
13501   if (!IN_LEV_FIELD(test_x, test_y))
13502     return;
13503
13504   test_move_dir =
13505     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13506
13507   test_element = Tile[test_x][test_y];
13508
13509   if (test_move_dir != bad_move_dir)
13510   {
13511     // good thing can be player or penguin that does not move away
13512     if (IS_PLAYER(test_x, test_y))
13513     {
13514       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13515
13516       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13517          player as being hit when he is moving towards the bad thing, because
13518          the "get hit by" condition would be lost after the player stops) */
13519       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13520         return;         // player moves away from bad thing
13521
13522       kill_x = test_x;
13523       kill_y = test_y;
13524     }
13525     else if (test_element == EL_PENGUIN)
13526     {
13527       kill_x = test_x;
13528       kill_y = test_y;
13529     }
13530   }
13531
13532   if (kill_x != -1 || kill_y != -1)
13533   {
13534     if (IS_PLAYER(kill_x, kill_y))
13535     {
13536       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13537
13538       if (player->shield_deadly_time_left > 0 &&
13539           !IS_INDESTRUCTIBLE(bad_element))
13540         Bang(bad_x, bad_y);
13541       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13542         KillPlayer(player);
13543     }
13544     else
13545       Bang(kill_x, kill_y);
13546   }
13547 }
13548
13549 void TestIfPlayerTouchesBadThing(int x, int y)
13550 {
13551   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13552 }
13553
13554 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13555 {
13556   TestIfGoodThingHitsBadThing(x, y, move_dir);
13557 }
13558
13559 void TestIfBadThingTouchesPlayer(int x, int y)
13560 {
13561   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13562 }
13563
13564 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13565 {
13566   TestIfBadThingHitsGoodThing(x, y, move_dir);
13567 }
13568
13569 void TestIfFriendTouchesBadThing(int x, int y)
13570 {
13571   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13572 }
13573
13574 void TestIfBadThingTouchesFriend(int x, int y)
13575 {
13576   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13577 }
13578
13579 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13580 {
13581   int i, kill_x = bad_x, kill_y = bad_y;
13582   static int xy[4][2] =
13583   {
13584     { 0, -1 },
13585     { -1, 0 },
13586     { +1, 0 },
13587     { 0, +1 }
13588   };
13589
13590   for (i = 0; i < NUM_DIRECTIONS; i++)
13591   {
13592     int x, y, element;
13593
13594     x = bad_x + xy[i][0];
13595     y = bad_y + xy[i][1];
13596     if (!IN_LEV_FIELD(x, y))
13597       continue;
13598
13599     element = Tile[x][y];
13600     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13601         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13602     {
13603       kill_x = x;
13604       kill_y = y;
13605       break;
13606     }
13607   }
13608
13609   if (kill_x != bad_x || kill_y != bad_y)
13610     Bang(bad_x, bad_y);
13611 }
13612
13613 void KillPlayer(struct PlayerInfo *player)
13614 {
13615   int jx = player->jx, jy = player->jy;
13616
13617   if (!player->active)
13618     return;
13619
13620 #if 0
13621   Debug("game:playing:KillPlayer",
13622         "0: killed == %d, active == %d, reanimated == %d",
13623         player->killed, player->active, player->reanimated);
13624 #endif
13625
13626   /* the following code was introduced to prevent an infinite loop when calling
13627      -> Bang()
13628      -> CheckTriggeredElementChangeExt()
13629      -> ExecuteCustomElementAction()
13630      -> KillPlayer()
13631      -> (infinitely repeating the above sequence of function calls)
13632      which occurs when killing the player while having a CE with the setting
13633      "kill player X when explosion of <player X>"; the solution using a new
13634      field "player->killed" was chosen for backwards compatibility, although
13635      clever use of the fields "player->active" etc. would probably also work */
13636 #if 1
13637   if (player->killed)
13638     return;
13639 #endif
13640
13641   player->killed = TRUE;
13642
13643   // remove accessible field at the player's position
13644   Tile[jx][jy] = EL_EMPTY;
13645
13646   // deactivate shield (else Bang()/Explode() would not work right)
13647   player->shield_normal_time_left = 0;
13648   player->shield_deadly_time_left = 0;
13649
13650 #if 0
13651   Debug("game:playing:KillPlayer",
13652         "1: killed == %d, active == %d, reanimated == %d",
13653         player->killed, player->active, player->reanimated);
13654 #endif
13655
13656   Bang(jx, jy);
13657
13658 #if 0
13659   Debug("game:playing:KillPlayer",
13660         "2: killed == %d, active == %d, reanimated == %d",
13661         player->killed, player->active, player->reanimated);
13662 #endif
13663
13664   if (player->reanimated)       // killed player may have been reanimated
13665     player->killed = player->reanimated = FALSE;
13666   else
13667     BuryPlayer(player);
13668 }
13669
13670 static void KillPlayerUnlessEnemyProtected(int x, int y)
13671 {
13672   if (!PLAYER_ENEMY_PROTECTED(x, y))
13673     KillPlayer(PLAYERINFO(x, y));
13674 }
13675
13676 static void KillPlayerUnlessExplosionProtected(int x, int y)
13677 {
13678   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13679     KillPlayer(PLAYERINFO(x, y));
13680 }
13681
13682 void BuryPlayer(struct PlayerInfo *player)
13683 {
13684   int jx = player->jx, jy = player->jy;
13685
13686   if (!player->active)
13687     return;
13688
13689   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13690   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13691
13692   RemovePlayer(player);
13693
13694   player->buried = TRUE;
13695
13696   if (game.all_players_gone)
13697     game.GameOver = TRUE;
13698 }
13699
13700 void RemovePlayer(struct PlayerInfo *player)
13701 {
13702   int jx = player->jx, jy = player->jy;
13703   int i, found = FALSE;
13704
13705   player->present = FALSE;
13706   player->active = FALSE;
13707
13708   // required for some CE actions (even if the player is not active anymore)
13709   player->MovPos = 0;
13710
13711   if (!ExplodeField[jx][jy])
13712     StorePlayer[jx][jy] = 0;
13713
13714   if (player->is_moving)
13715     TEST_DrawLevelField(player->last_jx, player->last_jy);
13716
13717   for (i = 0; i < MAX_PLAYERS; i++)
13718     if (stored_player[i].active)
13719       found = TRUE;
13720
13721   if (!found)
13722   {
13723     game.all_players_gone = TRUE;
13724     game.GameOver = TRUE;
13725   }
13726
13727   game.exit_x = game.robot_wheel_x = jx;
13728   game.exit_y = game.robot_wheel_y = jy;
13729 }
13730
13731 void ExitPlayer(struct PlayerInfo *player)
13732 {
13733   DrawPlayer(player);   // needed here only to cleanup last field
13734   RemovePlayer(player);
13735
13736   if (game.players_still_needed > 0)
13737     game.players_still_needed--;
13738 }
13739
13740 static void setFieldForSnapping(int x, int y, int element, int direction)
13741 {
13742   struct ElementInfo *ei = &element_info[element];
13743   int direction_bit = MV_DIR_TO_BIT(direction);
13744   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13745   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13746                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13747
13748   Tile[x][y] = EL_ELEMENT_SNAPPING;
13749   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13750
13751   ResetGfxAnimation(x, y);
13752
13753   GfxElement[x][y] = element;
13754   GfxAction[x][y] = action;
13755   GfxDir[x][y] = direction;
13756   GfxFrame[x][y] = -1;
13757 }
13758
13759 /*
13760   =============================================================================
13761   checkDiagonalPushing()
13762   -----------------------------------------------------------------------------
13763   check if diagonal input device direction results in pushing of object
13764   (by checking if the alternative direction is walkable, diggable, ...)
13765   =============================================================================
13766 */
13767
13768 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13769                                     int x, int y, int real_dx, int real_dy)
13770 {
13771   int jx, jy, dx, dy, xx, yy;
13772
13773   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13774     return TRUE;
13775
13776   // diagonal direction: check alternative direction
13777   jx = player->jx;
13778   jy = player->jy;
13779   dx = x - jx;
13780   dy = y - jy;
13781   xx = jx + (dx == 0 ? real_dx : 0);
13782   yy = jy + (dy == 0 ? real_dy : 0);
13783
13784   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13785 }
13786
13787 /*
13788   =============================================================================
13789   DigField()
13790   -----------------------------------------------------------------------------
13791   x, y:                 field next to player (non-diagonal) to try to dig to
13792   real_dx, real_dy:     direction as read from input device (can be diagonal)
13793   =============================================================================
13794 */
13795
13796 static int DigField(struct PlayerInfo *player,
13797                     int oldx, int oldy, int x, int y,
13798                     int real_dx, int real_dy, int mode)
13799 {
13800   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13801   boolean player_was_pushing = player->is_pushing;
13802   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13803   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13804   int jx = oldx, jy = oldy;
13805   int dx = x - jx, dy = y - jy;
13806   int nextx = x + dx, nexty = y + dy;
13807   int move_direction = (dx == -1 ? MV_LEFT  :
13808                         dx == +1 ? MV_RIGHT :
13809                         dy == -1 ? MV_UP    :
13810                         dy == +1 ? MV_DOWN  : MV_NONE);
13811   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13812   int dig_side = MV_DIR_OPPOSITE(move_direction);
13813   int old_element = Tile[jx][jy];
13814   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13815   int collect_count;
13816
13817   if (is_player)                // function can also be called by EL_PENGUIN
13818   {
13819     if (player->MovPos == 0)
13820     {
13821       player->is_digging = FALSE;
13822       player->is_collecting = FALSE;
13823     }
13824
13825     if (player->MovPos == 0)    // last pushing move finished
13826       player->is_pushing = FALSE;
13827
13828     if (mode == DF_NO_PUSH)     // player just stopped pushing
13829     {
13830       player->is_switching = FALSE;
13831       player->push_delay = -1;
13832
13833       return MP_NO_ACTION;
13834     }
13835   }
13836
13837   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13838     old_element = Back[jx][jy];
13839
13840   // in case of element dropped at player position, check background
13841   else if (Back[jx][jy] != EL_EMPTY &&
13842            game.engine_version >= VERSION_IDENT(2,2,0,0))
13843     old_element = Back[jx][jy];
13844
13845   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13846     return MP_NO_ACTION;        // field has no opening in this direction
13847
13848   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13849     return MP_NO_ACTION;        // field has no opening in this direction
13850
13851   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13852   {
13853     SplashAcid(x, y);
13854
13855     Tile[jx][jy] = player->artwork_element;
13856     InitMovingField(jx, jy, MV_DOWN);
13857     Store[jx][jy] = EL_ACID;
13858     ContinueMoving(jx, jy);
13859     BuryPlayer(player);
13860
13861     return MP_DONT_RUN_INTO;
13862   }
13863
13864   if (player_can_move && DONT_RUN_INTO(element))
13865   {
13866     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13867
13868     return MP_DONT_RUN_INTO;
13869   }
13870
13871   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13872     return MP_NO_ACTION;
13873
13874   collect_count = element_info[element].collect_count_initial;
13875
13876   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13877     return MP_NO_ACTION;
13878
13879   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13880     player_can_move = player_can_move_or_snap;
13881
13882   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13883       game.engine_version >= VERSION_IDENT(2,2,0,0))
13884   {
13885     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13886                                player->index_bit, dig_side);
13887     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13888                                         player->index_bit, dig_side);
13889
13890     if (element == EL_DC_LANDMINE)
13891       Bang(x, y);
13892
13893     if (Tile[x][y] != element)          // field changed by snapping
13894       return MP_ACTION;
13895
13896     return MP_NO_ACTION;
13897   }
13898
13899   if (player->gravity && is_player && !player->is_auto_moving &&
13900       canFallDown(player) && move_direction != MV_DOWN &&
13901       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13902     return MP_NO_ACTION;        // player cannot walk here due to gravity
13903
13904   if (player_can_move &&
13905       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13906   {
13907     int sound_element = SND_ELEMENT(element);
13908     int sound_action = ACTION_WALKING;
13909
13910     if (IS_RND_GATE(element))
13911     {
13912       if (!player->key[RND_GATE_NR(element)])
13913         return MP_NO_ACTION;
13914     }
13915     else if (IS_RND_GATE_GRAY(element))
13916     {
13917       if (!player->key[RND_GATE_GRAY_NR(element)])
13918         return MP_NO_ACTION;
13919     }
13920     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13921     {
13922       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13923         return MP_NO_ACTION;
13924     }
13925     else if (element == EL_EXIT_OPEN ||
13926              element == EL_EM_EXIT_OPEN ||
13927              element == EL_EM_EXIT_OPENING ||
13928              element == EL_STEEL_EXIT_OPEN ||
13929              element == EL_EM_STEEL_EXIT_OPEN ||
13930              element == EL_EM_STEEL_EXIT_OPENING ||
13931              element == EL_SP_EXIT_OPEN ||
13932              element == EL_SP_EXIT_OPENING)
13933     {
13934       sound_action = ACTION_PASSING;    // player is passing exit
13935     }
13936     else if (element == EL_EMPTY)
13937     {
13938       sound_action = ACTION_MOVING;             // nothing to walk on
13939     }
13940
13941     // play sound from background or player, whatever is available
13942     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13943       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13944     else
13945       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13946   }
13947   else if (player_can_move &&
13948            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13949   {
13950     if (!ACCESS_FROM(element, opposite_direction))
13951       return MP_NO_ACTION;      // field not accessible from this direction
13952
13953     if (CAN_MOVE(element))      // only fixed elements can be passed!
13954       return MP_NO_ACTION;
13955
13956     if (IS_EM_GATE(element))
13957     {
13958       if (!player->key[EM_GATE_NR(element)])
13959         return MP_NO_ACTION;
13960     }
13961     else if (IS_EM_GATE_GRAY(element))
13962     {
13963       if (!player->key[EM_GATE_GRAY_NR(element)])
13964         return MP_NO_ACTION;
13965     }
13966     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13967     {
13968       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13969         return MP_NO_ACTION;
13970     }
13971     else if (IS_EMC_GATE(element))
13972     {
13973       if (!player->key[EMC_GATE_NR(element)])
13974         return MP_NO_ACTION;
13975     }
13976     else if (IS_EMC_GATE_GRAY(element))
13977     {
13978       if (!player->key[EMC_GATE_GRAY_NR(element)])
13979         return MP_NO_ACTION;
13980     }
13981     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13982     {
13983       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13984         return MP_NO_ACTION;
13985     }
13986     else if (element == EL_DC_GATE_WHITE ||
13987              element == EL_DC_GATE_WHITE_GRAY ||
13988              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13989     {
13990       if (player->num_white_keys == 0)
13991         return MP_NO_ACTION;
13992
13993       player->num_white_keys--;
13994     }
13995     else if (IS_SP_PORT(element))
13996     {
13997       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13998           element == EL_SP_GRAVITY_PORT_RIGHT ||
13999           element == EL_SP_GRAVITY_PORT_UP ||
14000           element == EL_SP_GRAVITY_PORT_DOWN)
14001         player->gravity = !player->gravity;
14002       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14003                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14004                element == EL_SP_GRAVITY_ON_PORT_UP ||
14005                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14006         player->gravity = TRUE;
14007       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14008                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14009                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14010                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14011         player->gravity = FALSE;
14012     }
14013
14014     // automatically move to the next field with double speed
14015     player->programmed_action = move_direction;
14016
14017     if (player->move_delay_reset_counter == 0)
14018     {
14019       player->move_delay_reset_counter = 2;     // two double speed steps
14020
14021       DOUBLE_PLAYER_SPEED(player);
14022     }
14023
14024     PlayLevelSoundAction(x, y, ACTION_PASSING);
14025   }
14026   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14027   {
14028     RemoveField(x, y);
14029
14030     if (mode != DF_SNAP)
14031     {
14032       GfxElement[x][y] = GFX_ELEMENT(element);
14033       player->is_digging = TRUE;
14034     }
14035
14036     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14037
14038     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14039                                         player->index_bit, dig_side);
14040
14041     // if digging triggered player relocation, finish digging tile
14042     if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14043       setFieldForSnapping(x, y, element, move_direction);
14044
14045     if (mode == DF_SNAP)
14046     {
14047       if (level.block_snap_field)
14048         setFieldForSnapping(x, y, element, move_direction);
14049       else
14050         TestIfElementTouchesCustomElement(x, y);        // for empty space
14051
14052       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14053                                           player->index_bit, dig_side);
14054     }
14055   }
14056   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14057   {
14058     RemoveField(x, y);
14059
14060     if (is_player && mode != DF_SNAP)
14061     {
14062       GfxElement[x][y] = element;
14063       player->is_collecting = TRUE;
14064     }
14065
14066     if (element == EL_SPEED_PILL)
14067     {
14068       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14069     }
14070     else if (element == EL_EXTRA_TIME && level.time > 0)
14071     {
14072       TimeLeft += level.extra_time;
14073
14074       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14075
14076       DisplayGameControlValues();
14077     }
14078     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14079     {
14080       player->shield_normal_time_left += level.shield_normal_time;
14081       if (element == EL_SHIELD_DEADLY)
14082         player->shield_deadly_time_left += level.shield_deadly_time;
14083     }
14084     else if (element == EL_DYNAMITE ||
14085              element == EL_EM_DYNAMITE ||
14086              element == EL_SP_DISK_RED)
14087     {
14088       if (player->inventory_size < MAX_INVENTORY_SIZE)
14089         player->inventory_element[player->inventory_size++] = element;
14090
14091       DrawGameDoorValues();
14092     }
14093     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14094     {
14095       player->dynabomb_count++;
14096       player->dynabombs_left++;
14097     }
14098     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14099     {
14100       player->dynabomb_size++;
14101     }
14102     else if (element == EL_DYNABOMB_INCREASE_POWER)
14103     {
14104       player->dynabomb_xl = TRUE;
14105     }
14106     else if (IS_KEY(element))
14107     {
14108       player->key[KEY_NR(element)] = TRUE;
14109
14110       DrawGameDoorValues();
14111     }
14112     else if (element == EL_DC_KEY_WHITE)
14113     {
14114       player->num_white_keys++;
14115
14116       // display white keys?
14117       // DrawGameDoorValues();
14118     }
14119     else if (IS_ENVELOPE(element))
14120     {
14121       player->show_envelope = element;
14122     }
14123     else if (element == EL_EMC_LENSES)
14124     {
14125       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14126
14127       RedrawAllInvisibleElementsForLenses();
14128     }
14129     else if (element == EL_EMC_MAGNIFIER)
14130     {
14131       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14132
14133       RedrawAllInvisibleElementsForMagnifier();
14134     }
14135     else if (IS_DROPPABLE(element) ||
14136              IS_THROWABLE(element))     // can be collected and dropped
14137     {
14138       int i;
14139
14140       if (collect_count == 0)
14141         player->inventory_infinite_element = element;
14142       else
14143         for (i = 0; i < collect_count; i++)
14144           if (player->inventory_size < MAX_INVENTORY_SIZE)
14145             player->inventory_element[player->inventory_size++] = element;
14146
14147       DrawGameDoorValues();
14148     }
14149     else if (collect_count > 0)
14150     {
14151       game.gems_still_needed -= collect_count;
14152       if (game.gems_still_needed < 0)
14153         game.gems_still_needed = 0;
14154
14155       game.snapshot.collected_item = TRUE;
14156
14157       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14158
14159       DisplayGameControlValues();
14160     }
14161
14162     RaiseScoreElement(element);
14163     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14164
14165     if (is_player)
14166     {
14167       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14168                                           player->index_bit, dig_side);
14169
14170       // if collecting triggered player relocation, finish collecting tile
14171       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14172         setFieldForSnapping(x, y, element, move_direction);
14173     }
14174
14175     if (mode == DF_SNAP)
14176     {
14177       if (level.block_snap_field)
14178         setFieldForSnapping(x, y, element, move_direction);
14179       else
14180         TestIfElementTouchesCustomElement(x, y);        // for empty space
14181
14182       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14183                                           player->index_bit, dig_side);
14184     }
14185   }
14186   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14187   {
14188     if (mode == DF_SNAP && element != EL_BD_ROCK)
14189       return MP_NO_ACTION;
14190
14191     if (CAN_FALL(element) && dy)
14192       return MP_NO_ACTION;
14193
14194     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14195         !(element == EL_SPRING && level.use_spring_bug))
14196       return MP_NO_ACTION;
14197
14198     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14199         ((move_direction & MV_VERTICAL &&
14200           ((element_info[element].move_pattern & MV_LEFT &&
14201             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14202            (element_info[element].move_pattern & MV_RIGHT &&
14203             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14204          (move_direction & MV_HORIZONTAL &&
14205           ((element_info[element].move_pattern & MV_UP &&
14206             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14207            (element_info[element].move_pattern & MV_DOWN &&
14208             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14209       return MP_NO_ACTION;
14210
14211     // do not push elements already moving away faster than player
14212     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14213         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14214       return MP_NO_ACTION;
14215
14216     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14217     {
14218       if (player->push_delay_value == -1 || !player_was_pushing)
14219         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14220     }
14221     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14222     {
14223       if (player->push_delay_value == -1)
14224         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14225     }
14226     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14227     {
14228       if (!player->is_pushing)
14229         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14230     }
14231
14232     player->is_pushing = TRUE;
14233     player->is_active = TRUE;
14234
14235     if (!(IN_LEV_FIELD(nextx, nexty) &&
14236           (IS_FREE(nextx, nexty) ||
14237            (IS_SB_ELEMENT(element) &&
14238             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14239            (IS_CUSTOM_ELEMENT(element) &&
14240             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14241       return MP_NO_ACTION;
14242
14243     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14244       return MP_NO_ACTION;
14245
14246     if (player->push_delay == -1)       // new pushing; restart delay
14247       player->push_delay = 0;
14248
14249     if (player->push_delay < player->push_delay_value &&
14250         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14251         element != EL_SPRING && element != EL_BALLOON)
14252     {
14253       // make sure that there is no move delay before next try to push
14254       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14255         player->move_delay = 0;
14256
14257       return MP_NO_ACTION;
14258     }
14259
14260     if (IS_CUSTOM_ELEMENT(element) &&
14261         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14262     {
14263       if (!DigFieldByCE(nextx, nexty, element))
14264         return MP_NO_ACTION;
14265     }
14266
14267     if (IS_SB_ELEMENT(element))
14268     {
14269       boolean sokoban_task_solved = FALSE;
14270
14271       if (element == EL_SOKOBAN_FIELD_FULL)
14272       {
14273         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14274
14275         IncrementSokobanFieldsNeeded();
14276         IncrementSokobanObjectsNeeded();
14277       }
14278
14279       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14280       {
14281         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14282
14283         DecrementSokobanFieldsNeeded();
14284         DecrementSokobanObjectsNeeded();
14285
14286         // sokoban object was pushed from empty field to sokoban field
14287         if (Back[x][y] == EL_EMPTY)
14288           sokoban_task_solved = TRUE;
14289       }
14290
14291       Tile[x][y] = EL_SOKOBAN_OBJECT;
14292
14293       if (Back[x][y] == Back[nextx][nexty])
14294         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14295       else if (Back[x][y] != 0)
14296         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14297                                     ACTION_EMPTYING);
14298       else
14299         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14300                                     ACTION_FILLING);
14301
14302       if (sokoban_task_solved &&
14303           game.sokoban_fields_still_needed == 0 &&
14304           game.sokoban_objects_still_needed == 0 &&
14305           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14306       {
14307         game.players_still_needed = 0;
14308
14309         LevelSolved();
14310
14311         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14312       }
14313     }
14314     else
14315       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14316
14317     InitMovingField(x, y, move_direction);
14318     GfxAction[x][y] = ACTION_PUSHING;
14319
14320     if (mode == DF_SNAP)
14321       ContinueMoving(x, y);
14322     else
14323       MovPos[x][y] = (dx != 0 ? dx : dy);
14324
14325     Pushed[x][y] = TRUE;
14326     Pushed[nextx][nexty] = TRUE;
14327
14328     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14329       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14330     else
14331       player->push_delay_value = -1;    // get new value later
14332
14333     // check for element change _after_ element has been pushed
14334     if (game.use_change_when_pushing_bug)
14335     {
14336       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14337                                  player->index_bit, dig_side);
14338       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14339                                           player->index_bit, dig_side);
14340     }
14341   }
14342   else if (IS_SWITCHABLE(element))
14343   {
14344     if (PLAYER_SWITCHING(player, x, y))
14345     {
14346       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14347                                           player->index_bit, dig_side);
14348
14349       return MP_ACTION;
14350     }
14351
14352     player->is_switching = TRUE;
14353     player->switch_x = x;
14354     player->switch_y = y;
14355
14356     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14357
14358     if (element == EL_ROBOT_WHEEL)
14359     {
14360       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14361
14362       game.robot_wheel_x = x;
14363       game.robot_wheel_y = y;
14364       game.robot_wheel_active = TRUE;
14365
14366       TEST_DrawLevelField(x, y);
14367     }
14368     else if (element == EL_SP_TERMINAL)
14369     {
14370       int xx, yy;
14371
14372       SCAN_PLAYFIELD(xx, yy)
14373       {
14374         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14375         {
14376           Bang(xx, yy);
14377         }
14378         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14379         {
14380           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14381
14382           ResetGfxAnimation(xx, yy);
14383           TEST_DrawLevelField(xx, yy);
14384         }
14385       }
14386     }
14387     else if (IS_BELT_SWITCH(element))
14388     {
14389       ToggleBeltSwitch(x, y);
14390     }
14391     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14392              element == EL_SWITCHGATE_SWITCH_DOWN ||
14393              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14394              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14395     {
14396       ToggleSwitchgateSwitch(x, y);
14397     }
14398     else if (element == EL_LIGHT_SWITCH ||
14399              element == EL_LIGHT_SWITCH_ACTIVE)
14400     {
14401       ToggleLightSwitch(x, y);
14402     }
14403     else if (element == EL_TIMEGATE_SWITCH ||
14404              element == EL_DC_TIMEGATE_SWITCH)
14405     {
14406       ActivateTimegateSwitch(x, y);
14407     }
14408     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14409              element == EL_BALLOON_SWITCH_RIGHT ||
14410              element == EL_BALLOON_SWITCH_UP    ||
14411              element == EL_BALLOON_SWITCH_DOWN  ||
14412              element == EL_BALLOON_SWITCH_NONE  ||
14413              element == EL_BALLOON_SWITCH_ANY)
14414     {
14415       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14416                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14417                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14418                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14419                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14420                              move_direction);
14421     }
14422     else if (element == EL_LAMP)
14423     {
14424       Tile[x][y] = EL_LAMP_ACTIVE;
14425       game.lights_still_needed--;
14426
14427       ResetGfxAnimation(x, y);
14428       TEST_DrawLevelField(x, y);
14429     }
14430     else if (element == EL_TIME_ORB_FULL)
14431     {
14432       Tile[x][y] = EL_TIME_ORB_EMPTY;
14433
14434       if (level.time > 0 || level.use_time_orb_bug)
14435       {
14436         TimeLeft += level.time_orb_time;
14437         game.no_time_limit = FALSE;
14438
14439         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14440
14441         DisplayGameControlValues();
14442       }
14443
14444       ResetGfxAnimation(x, y);
14445       TEST_DrawLevelField(x, y);
14446     }
14447     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14448              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14449     {
14450       int xx, yy;
14451
14452       game.ball_active = !game.ball_active;
14453
14454       SCAN_PLAYFIELD(xx, yy)
14455       {
14456         int e = Tile[xx][yy];
14457
14458         if (game.ball_active)
14459         {
14460           if (e == EL_EMC_MAGIC_BALL)
14461             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14462           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14463             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14464         }
14465         else
14466         {
14467           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14468             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14469           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14470             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14471         }
14472       }
14473     }
14474
14475     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14476                                         player->index_bit, dig_side);
14477
14478     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14479                                         player->index_bit, dig_side);
14480
14481     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14482                                         player->index_bit, dig_side);
14483
14484     return MP_ACTION;
14485   }
14486   else
14487   {
14488     if (!PLAYER_SWITCHING(player, x, y))
14489     {
14490       player->is_switching = TRUE;
14491       player->switch_x = x;
14492       player->switch_y = y;
14493
14494       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14495                                  player->index_bit, dig_side);
14496       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14497                                           player->index_bit, dig_side);
14498
14499       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14500                                  player->index_bit, dig_side);
14501       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14502                                           player->index_bit, dig_side);
14503     }
14504
14505     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14506                                player->index_bit, dig_side);
14507     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14508                                         player->index_bit, dig_side);
14509
14510     return MP_NO_ACTION;
14511   }
14512
14513   player->push_delay = -1;
14514
14515   if (is_player)                // function can also be called by EL_PENGUIN
14516   {
14517     if (Tile[x][y] != element)          // really digged/collected something
14518     {
14519       player->is_collecting = !player->is_digging;
14520       player->is_active = TRUE;
14521     }
14522   }
14523
14524   return MP_MOVING;
14525 }
14526
14527 static boolean DigFieldByCE(int x, int y, int digging_element)
14528 {
14529   int element = Tile[x][y];
14530
14531   if (!IS_FREE(x, y))
14532   {
14533     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14534                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14535                   ACTION_BREAKING);
14536
14537     // no element can dig solid indestructible elements
14538     if (IS_INDESTRUCTIBLE(element) &&
14539         !IS_DIGGABLE(element) &&
14540         !IS_COLLECTIBLE(element))
14541       return FALSE;
14542
14543     if (AmoebaNr[x][y] &&
14544         (element == EL_AMOEBA_FULL ||
14545          element == EL_BD_AMOEBA ||
14546          element == EL_AMOEBA_GROWING))
14547     {
14548       AmoebaCnt[AmoebaNr[x][y]]--;
14549       AmoebaCnt2[AmoebaNr[x][y]]--;
14550     }
14551
14552     if (IS_MOVING(x, y))
14553       RemoveMovingField(x, y);
14554     else
14555     {
14556       RemoveField(x, y);
14557       TEST_DrawLevelField(x, y);
14558     }
14559
14560     // if digged element was about to explode, prevent the explosion
14561     ExplodeField[x][y] = EX_TYPE_NONE;
14562
14563     PlayLevelSoundAction(x, y, action);
14564   }
14565
14566   Store[x][y] = EL_EMPTY;
14567
14568   // this makes it possible to leave the removed element again
14569   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14570     Store[x][y] = element;
14571
14572   return TRUE;
14573 }
14574
14575 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14576 {
14577   int jx = player->jx, jy = player->jy;
14578   int x = jx + dx, y = jy + dy;
14579   int snap_direction = (dx == -1 ? MV_LEFT  :
14580                         dx == +1 ? MV_RIGHT :
14581                         dy == -1 ? MV_UP    :
14582                         dy == +1 ? MV_DOWN  : MV_NONE);
14583   boolean can_continue_snapping = (level.continuous_snapping &&
14584                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14585
14586   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14587     return FALSE;
14588
14589   if (!player->active || !IN_LEV_FIELD(x, y))
14590     return FALSE;
14591
14592   if (dx && dy)
14593     return FALSE;
14594
14595   if (!dx && !dy)
14596   {
14597     if (player->MovPos == 0)
14598       player->is_pushing = FALSE;
14599
14600     player->is_snapping = FALSE;
14601
14602     if (player->MovPos == 0)
14603     {
14604       player->is_moving = FALSE;
14605       player->is_digging = FALSE;
14606       player->is_collecting = FALSE;
14607     }
14608
14609     return FALSE;
14610   }
14611
14612   // prevent snapping with already pressed snap key when not allowed
14613   if (player->is_snapping && !can_continue_snapping)
14614     return FALSE;
14615
14616   player->MovDir = snap_direction;
14617
14618   if (player->MovPos == 0)
14619   {
14620     player->is_moving = FALSE;
14621     player->is_digging = FALSE;
14622     player->is_collecting = FALSE;
14623   }
14624
14625   player->is_dropping = FALSE;
14626   player->is_dropping_pressed = FALSE;
14627   player->drop_pressed_delay = 0;
14628
14629   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14630     return FALSE;
14631
14632   player->is_snapping = TRUE;
14633   player->is_active = TRUE;
14634
14635   if (player->MovPos == 0)
14636   {
14637     player->is_moving = FALSE;
14638     player->is_digging = FALSE;
14639     player->is_collecting = FALSE;
14640   }
14641
14642   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14643     TEST_DrawLevelField(player->last_jx, player->last_jy);
14644
14645   TEST_DrawLevelField(x, y);
14646
14647   return TRUE;
14648 }
14649
14650 static boolean DropElement(struct PlayerInfo *player)
14651 {
14652   int old_element, new_element;
14653   int dropx = player->jx, dropy = player->jy;
14654   int drop_direction = player->MovDir;
14655   int drop_side = drop_direction;
14656   int drop_element = get_next_dropped_element(player);
14657
14658   /* do not drop an element on top of another element; when holding drop key
14659      pressed without moving, dropped element must move away before the next
14660      element can be dropped (this is especially important if the next element
14661      is dynamite, which can be placed on background for historical reasons) */
14662   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14663     return MP_ACTION;
14664
14665   if (IS_THROWABLE(drop_element))
14666   {
14667     dropx += GET_DX_FROM_DIR(drop_direction);
14668     dropy += GET_DY_FROM_DIR(drop_direction);
14669
14670     if (!IN_LEV_FIELD(dropx, dropy))
14671       return FALSE;
14672   }
14673
14674   old_element = Tile[dropx][dropy];     // old element at dropping position
14675   new_element = drop_element;           // default: no change when dropping
14676
14677   // check if player is active, not moving and ready to drop
14678   if (!player->active || player->MovPos || player->drop_delay > 0)
14679     return FALSE;
14680
14681   // check if player has anything that can be dropped
14682   if (new_element == EL_UNDEFINED)
14683     return FALSE;
14684
14685   // only set if player has anything that can be dropped
14686   player->is_dropping_pressed = TRUE;
14687
14688   // check if drop key was pressed long enough for EM style dynamite
14689   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14690     return FALSE;
14691
14692   // check if anything can be dropped at the current position
14693   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14694     return FALSE;
14695
14696   // collected custom elements can only be dropped on empty fields
14697   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14698     return FALSE;
14699
14700   if (old_element != EL_EMPTY)
14701     Back[dropx][dropy] = old_element;   // store old element on this field
14702
14703   ResetGfxAnimation(dropx, dropy);
14704   ResetRandomAnimationValue(dropx, dropy);
14705
14706   if (player->inventory_size > 0 ||
14707       player->inventory_infinite_element != EL_UNDEFINED)
14708   {
14709     if (player->inventory_size > 0)
14710     {
14711       player->inventory_size--;
14712
14713       DrawGameDoorValues();
14714
14715       if (new_element == EL_DYNAMITE)
14716         new_element = EL_DYNAMITE_ACTIVE;
14717       else if (new_element == EL_EM_DYNAMITE)
14718         new_element = EL_EM_DYNAMITE_ACTIVE;
14719       else if (new_element == EL_SP_DISK_RED)
14720         new_element = EL_SP_DISK_RED_ACTIVE;
14721     }
14722
14723     Tile[dropx][dropy] = new_element;
14724
14725     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14726       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14727                           el2img(Tile[dropx][dropy]), 0);
14728
14729     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14730
14731     // needed if previous element just changed to "empty" in the last frame
14732     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14733
14734     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14735                                player->index_bit, drop_side);
14736     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14737                                         CE_PLAYER_DROPS_X,
14738                                         player->index_bit, drop_side);
14739
14740     TestIfElementTouchesCustomElement(dropx, dropy);
14741   }
14742   else          // player is dropping a dyna bomb
14743   {
14744     player->dynabombs_left--;
14745
14746     Tile[dropx][dropy] = new_element;
14747
14748     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14749       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14750                           el2img(Tile[dropx][dropy]), 0);
14751
14752     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14753   }
14754
14755   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14756     InitField_WithBug1(dropx, dropy, FALSE);
14757
14758   new_element = Tile[dropx][dropy];     // element might have changed
14759
14760   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14761       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14762   {
14763     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14764       MovDir[dropx][dropy] = drop_direction;
14765
14766     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14767
14768     // do not cause impact style collision by dropping elements that can fall
14769     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14770   }
14771
14772   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14773   player->is_dropping = TRUE;
14774
14775   player->drop_pressed_delay = 0;
14776   player->is_dropping_pressed = FALSE;
14777
14778   player->drop_x = dropx;
14779   player->drop_y = dropy;
14780
14781   return TRUE;
14782 }
14783
14784 // ----------------------------------------------------------------------------
14785 // game sound playing functions
14786 // ----------------------------------------------------------------------------
14787
14788 static int *loop_sound_frame = NULL;
14789 static int *loop_sound_volume = NULL;
14790
14791 void InitPlayLevelSound(void)
14792 {
14793   int num_sounds = getSoundListSize();
14794
14795   checked_free(loop_sound_frame);
14796   checked_free(loop_sound_volume);
14797
14798   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14799   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14800 }
14801
14802 static void PlayLevelSound(int x, int y, int nr)
14803 {
14804   int sx = SCREENX(x), sy = SCREENY(y);
14805   int volume, stereo_position;
14806   int max_distance = 8;
14807   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14808
14809   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14810       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14811     return;
14812
14813   if (!IN_LEV_FIELD(x, y) ||
14814       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14815       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14816     return;
14817
14818   volume = SOUND_MAX_VOLUME;
14819
14820   if (!IN_SCR_FIELD(sx, sy))
14821   {
14822     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14823     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14824
14825     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14826   }
14827
14828   stereo_position = (SOUND_MAX_LEFT +
14829                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14830                      (SCR_FIELDX + 2 * max_distance));
14831
14832   if (IS_LOOP_SOUND(nr))
14833   {
14834     /* This assures that quieter loop sounds do not overwrite louder ones,
14835        while restarting sound volume comparison with each new game frame. */
14836
14837     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14838       return;
14839
14840     loop_sound_volume[nr] = volume;
14841     loop_sound_frame[nr] = FrameCounter;
14842   }
14843
14844   PlaySoundExt(nr, volume, stereo_position, type);
14845 }
14846
14847 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14848 {
14849   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14850                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14851                  y < LEVELY(BY1) ? LEVELY(BY1) :
14852                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14853                  sound_action);
14854 }
14855
14856 static void PlayLevelSoundAction(int x, int y, int action)
14857 {
14858   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
14859 }
14860
14861 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14862 {
14863   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14864
14865   if (sound_effect != SND_UNDEFINED)
14866     PlayLevelSound(x, y, sound_effect);
14867 }
14868
14869 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14870                                               int action)
14871 {
14872   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14873
14874   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14875     PlayLevelSound(x, y, sound_effect);
14876 }
14877
14878 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14879 {
14880   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14881
14882   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14883     PlayLevelSound(x, y, sound_effect);
14884 }
14885
14886 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14887 {
14888   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14889
14890   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14891     StopSound(sound_effect);
14892 }
14893
14894 static int getLevelMusicNr(void)
14895 {
14896   if (levelset.music[level_nr] != MUS_UNDEFINED)
14897     return levelset.music[level_nr];            // from config file
14898   else
14899     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14900 }
14901
14902 static void FadeLevelSounds(void)
14903 {
14904   FadeSounds();
14905 }
14906
14907 static void FadeLevelMusic(void)
14908 {
14909   int music_nr = getLevelMusicNr();
14910   char *curr_music = getCurrentlyPlayingMusicFilename();
14911   char *next_music = getMusicInfoEntryFilename(music_nr);
14912
14913   if (!strEqual(curr_music, next_music))
14914     FadeMusic();
14915 }
14916
14917 void FadeLevelSoundsAndMusic(void)
14918 {
14919   FadeLevelSounds();
14920   FadeLevelMusic();
14921 }
14922
14923 static void PlayLevelMusic(void)
14924 {
14925   int music_nr = getLevelMusicNr();
14926   char *curr_music = getCurrentlyPlayingMusicFilename();
14927   char *next_music = getMusicInfoEntryFilename(music_nr);
14928
14929   if (!strEqual(curr_music, next_music))
14930     PlayMusicLoop(music_nr);
14931 }
14932
14933 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14934 {
14935   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14936   int offset = 0;
14937   int x = xx - offset;
14938   int y = yy - offset;
14939
14940   switch (sample)
14941   {
14942     case SOUND_blank:
14943       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14944       break;
14945
14946     case SOUND_roll:
14947       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14948       break;
14949
14950     case SOUND_stone:
14951       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14952       break;
14953
14954     case SOUND_nut:
14955       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14956       break;
14957
14958     case SOUND_crack:
14959       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14960       break;
14961
14962     case SOUND_bug:
14963       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14964       break;
14965
14966     case SOUND_tank:
14967       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14968       break;
14969
14970     case SOUND_android_clone:
14971       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14972       break;
14973
14974     case SOUND_android_move:
14975       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14976       break;
14977
14978     case SOUND_spring:
14979       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14980       break;
14981
14982     case SOUND_slurp:
14983       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14984       break;
14985
14986     case SOUND_eater:
14987       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14988       break;
14989
14990     case SOUND_eater_eat:
14991       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14992       break;
14993
14994     case SOUND_alien:
14995       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14996       break;
14997
14998     case SOUND_collect:
14999       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15000       break;
15001
15002     case SOUND_diamond:
15003       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15004       break;
15005
15006     case SOUND_squash:
15007       // !!! CHECK THIS !!!
15008 #if 1
15009       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15010 #else
15011       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15012 #endif
15013       break;
15014
15015     case SOUND_wonderfall:
15016       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15017       break;
15018
15019     case SOUND_drip:
15020       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15021       break;
15022
15023     case SOUND_push:
15024       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15025       break;
15026
15027     case SOUND_dirt:
15028       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15029       break;
15030
15031     case SOUND_acid:
15032       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15033       break;
15034
15035     case SOUND_ball:
15036       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15037       break;
15038
15039     case SOUND_slide:
15040       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15041       break;
15042
15043     case SOUND_wonder:
15044       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15045       break;
15046
15047     case SOUND_door:
15048       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15049       break;
15050
15051     case SOUND_exit_open:
15052       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15053       break;
15054
15055     case SOUND_exit_leave:
15056       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15057       break;
15058
15059     case SOUND_dynamite:
15060       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15061       break;
15062
15063     case SOUND_tick:
15064       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15065       break;
15066
15067     case SOUND_press:
15068       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15069       break;
15070
15071     case SOUND_wheel:
15072       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15073       break;
15074
15075     case SOUND_boom:
15076       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15077       break;
15078
15079     case SOUND_die:
15080       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15081       break;
15082
15083     case SOUND_time:
15084       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15085       break;
15086
15087     default:
15088       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15089       break;
15090   }
15091 }
15092
15093 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15094 {
15095   int element = map_element_SP_to_RND(element_sp);
15096   int action = map_action_SP_to_RND(action_sp);
15097   int offset = (setup.sp_show_border_elements ? 0 : 1);
15098   int x = xx - offset;
15099   int y = yy - offset;
15100
15101   PlayLevelSoundElementAction(x, y, element, action);
15102 }
15103
15104 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15105 {
15106   int element = map_element_MM_to_RND(element_mm);
15107   int action = map_action_MM_to_RND(action_mm);
15108   int offset = 0;
15109   int x = xx - offset;
15110   int y = yy - offset;
15111
15112   if (!IS_MM_ELEMENT(element))
15113     element = EL_MM_DEFAULT;
15114
15115   PlayLevelSoundElementAction(x, y, element, action);
15116 }
15117
15118 void PlaySound_MM(int sound_mm)
15119 {
15120   int sound = map_sound_MM_to_RND(sound_mm);
15121
15122   if (sound == SND_UNDEFINED)
15123     return;
15124
15125   PlaySound(sound);
15126 }
15127
15128 void PlaySoundLoop_MM(int sound_mm)
15129 {
15130   int sound = map_sound_MM_to_RND(sound_mm);
15131
15132   if (sound == SND_UNDEFINED)
15133     return;
15134
15135   PlaySoundLoop(sound);
15136 }
15137
15138 void StopSound_MM(int sound_mm)
15139 {
15140   int sound = map_sound_MM_to_RND(sound_mm);
15141
15142   if (sound == SND_UNDEFINED)
15143     return;
15144
15145   StopSound(sound);
15146 }
15147
15148 void RaiseScore(int value)
15149 {
15150   game.score += value;
15151
15152   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15153
15154   DisplayGameControlValues();
15155 }
15156
15157 void RaiseScoreElement(int element)
15158 {
15159   switch (element)
15160   {
15161     case EL_EMERALD:
15162     case EL_BD_DIAMOND:
15163     case EL_EMERALD_YELLOW:
15164     case EL_EMERALD_RED:
15165     case EL_EMERALD_PURPLE:
15166     case EL_SP_INFOTRON:
15167       RaiseScore(level.score[SC_EMERALD]);
15168       break;
15169     case EL_DIAMOND:
15170       RaiseScore(level.score[SC_DIAMOND]);
15171       break;
15172     case EL_CRYSTAL:
15173       RaiseScore(level.score[SC_CRYSTAL]);
15174       break;
15175     case EL_PEARL:
15176       RaiseScore(level.score[SC_PEARL]);
15177       break;
15178     case EL_BUG:
15179     case EL_BD_BUTTERFLY:
15180     case EL_SP_ELECTRON:
15181       RaiseScore(level.score[SC_BUG]);
15182       break;
15183     case EL_SPACESHIP:
15184     case EL_BD_FIREFLY:
15185     case EL_SP_SNIKSNAK:
15186       RaiseScore(level.score[SC_SPACESHIP]);
15187       break;
15188     case EL_YAMYAM:
15189     case EL_DARK_YAMYAM:
15190       RaiseScore(level.score[SC_YAMYAM]);
15191       break;
15192     case EL_ROBOT:
15193       RaiseScore(level.score[SC_ROBOT]);
15194       break;
15195     case EL_PACMAN:
15196       RaiseScore(level.score[SC_PACMAN]);
15197       break;
15198     case EL_NUT:
15199       RaiseScore(level.score[SC_NUT]);
15200       break;
15201     case EL_DYNAMITE:
15202     case EL_EM_DYNAMITE:
15203     case EL_SP_DISK_RED:
15204     case EL_DYNABOMB_INCREASE_NUMBER:
15205     case EL_DYNABOMB_INCREASE_SIZE:
15206     case EL_DYNABOMB_INCREASE_POWER:
15207       RaiseScore(level.score[SC_DYNAMITE]);
15208       break;
15209     case EL_SHIELD_NORMAL:
15210     case EL_SHIELD_DEADLY:
15211       RaiseScore(level.score[SC_SHIELD]);
15212       break;
15213     case EL_EXTRA_TIME:
15214       RaiseScore(level.extra_time_score);
15215       break;
15216     case EL_KEY_1:
15217     case EL_KEY_2:
15218     case EL_KEY_3:
15219     case EL_KEY_4:
15220     case EL_EM_KEY_1:
15221     case EL_EM_KEY_2:
15222     case EL_EM_KEY_3:
15223     case EL_EM_KEY_4:
15224     case EL_EMC_KEY_5:
15225     case EL_EMC_KEY_6:
15226     case EL_EMC_KEY_7:
15227     case EL_EMC_KEY_8:
15228     case EL_DC_KEY_WHITE:
15229       RaiseScore(level.score[SC_KEY]);
15230       break;
15231     default:
15232       RaiseScore(element_info[element].collect_score);
15233       break;
15234   }
15235 }
15236
15237 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15238 {
15239   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15240   {
15241     // closing door required in case of envelope style request dialogs
15242     if (!skip_request)
15243     {
15244       // prevent short reactivation of overlay buttons while closing door
15245       SetOverlayActive(FALSE);
15246
15247       CloseDoor(DOOR_CLOSE_1);
15248     }
15249
15250     if (network.enabled)
15251       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15252     else
15253     {
15254       if (quick_quit)
15255         FadeSkipNextFadeIn();
15256
15257       SetGameStatus(GAME_MODE_MAIN);
15258
15259       DrawMainMenu();
15260     }
15261   }
15262   else          // continue playing the game
15263   {
15264     if (tape.playing && tape.deactivate_display)
15265       TapeDeactivateDisplayOff(TRUE);
15266
15267     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15268
15269     if (tape.playing && tape.deactivate_display)
15270       TapeDeactivateDisplayOn();
15271   }
15272 }
15273
15274 void RequestQuitGame(boolean ask_if_really_quit)
15275 {
15276   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15277   boolean skip_request = game.all_players_gone || quick_quit;
15278
15279   RequestQuitGameExt(skip_request, quick_quit,
15280                      "Do you really want to quit the game?");
15281 }
15282
15283 void RequestRestartGame(char *message)
15284 {
15285   game.restart_game_message = NULL;
15286
15287   boolean has_started_game = hasStartedNetworkGame();
15288   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15289
15290   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15291   {
15292     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15293   }
15294   else
15295   {
15296     // needed in case of envelope request to close game panel
15297     CloseDoor(DOOR_CLOSE_1);
15298
15299     SetGameStatus(GAME_MODE_MAIN);
15300
15301     DrawMainMenu();
15302   }
15303 }
15304
15305 void CheckGameOver(void)
15306 {
15307   static boolean last_game_over = FALSE;
15308   static int game_over_delay = 0;
15309   int game_over_delay_value = 50;
15310   boolean game_over = checkGameFailed();
15311
15312   // do not handle game over if request dialog is already active
15313   if (game.request_active)
15314     return;
15315
15316   // do not ask to play again if game was never actually played
15317   if (!game.GamePlayed)
15318     return;
15319
15320   if (!game_over)
15321   {
15322     last_game_over = FALSE;
15323     game_over_delay = game_over_delay_value;
15324
15325     return;
15326   }
15327
15328   if (game_over_delay > 0)
15329   {
15330     game_over_delay--;
15331
15332     return;
15333   }
15334
15335   if (last_game_over != game_over)
15336     game.restart_game_message = (hasStartedNetworkGame() ?
15337                                  "Game over! Play it again?" :
15338                                  "Game over!");
15339
15340   last_game_over = game_over;
15341 }
15342
15343 boolean checkGameSolved(void)
15344 {
15345   // set for all game engines if level was solved
15346   return game.LevelSolved_GameEnd;
15347 }
15348
15349 boolean checkGameFailed(void)
15350 {
15351   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15352     return (game_em.game_over && !game_em.level_solved);
15353   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15354     return (game_sp.game_over && !game_sp.level_solved);
15355   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15356     return (game_mm.game_over && !game_mm.level_solved);
15357   else                          // GAME_ENGINE_TYPE_RND
15358     return (game.GameOver && !game.LevelSolved);
15359 }
15360
15361 boolean checkGameEnded(void)
15362 {
15363   return (checkGameSolved() || checkGameFailed());
15364 }
15365
15366
15367 // ----------------------------------------------------------------------------
15368 // random generator functions
15369 // ----------------------------------------------------------------------------
15370
15371 unsigned int InitEngineRandom_RND(int seed)
15372 {
15373   game.num_random_calls = 0;
15374
15375   return InitEngineRandom(seed);
15376 }
15377
15378 unsigned int RND(int max)
15379 {
15380   if (max > 0)
15381   {
15382     game.num_random_calls++;
15383
15384     return GetEngineRandom(max);
15385   }
15386
15387   return 0;
15388 }
15389
15390
15391 // ----------------------------------------------------------------------------
15392 // game engine snapshot handling functions
15393 // ----------------------------------------------------------------------------
15394
15395 struct EngineSnapshotInfo
15396 {
15397   // runtime values for custom element collect score
15398   int collect_score[NUM_CUSTOM_ELEMENTS];
15399
15400   // runtime values for group element choice position
15401   int choice_pos[NUM_GROUP_ELEMENTS];
15402
15403   // runtime values for belt position animations
15404   int belt_graphic[4][NUM_BELT_PARTS];
15405   int belt_anim_mode[4][NUM_BELT_PARTS];
15406 };
15407
15408 static struct EngineSnapshotInfo engine_snapshot_rnd;
15409 static char *snapshot_level_identifier = NULL;
15410 static int snapshot_level_nr = -1;
15411
15412 static void SaveEngineSnapshotValues_RND(void)
15413 {
15414   static int belt_base_active_element[4] =
15415   {
15416     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15417     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15418     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15419     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15420   };
15421   int i, j;
15422
15423   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15424   {
15425     int element = EL_CUSTOM_START + i;
15426
15427     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15428   }
15429
15430   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15431   {
15432     int element = EL_GROUP_START + i;
15433
15434     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15435   }
15436
15437   for (i = 0; i < 4; i++)
15438   {
15439     for (j = 0; j < NUM_BELT_PARTS; j++)
15440     {
15441       int element = belt_base_active_element[i] + j;
15442       int graphic = el2img(element);
15443       int anim_mode = graphic_info[graphic].anim_mode;
15444
15445       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15446       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15447     }
15448   }
15449 }
15450
15451 static void LoadEngineSnapshotValues_RND(void)
15452 {
15453   unsigned int num_random_calls = game.num_random_calls;
15454   int i, j;
15455
15456   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15457   {
15458     int element = EL_CUSTOM_START + i;
15459
15460     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15461   }
15462
15463   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15464   {
15465     int element = EL_GROUP_START + i;
15466
15467     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15468   }
15469
15470   for (i = 0; i < 4; i++)
15471   {
15472     for (j = 0; j < NUM_BELT_PARTS; j++)
15473     {
15474       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15475       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15476
15477       graphic_info[graphic].anim_mode = anim_mode;
15478     }
15479   }
15480
15481   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15482   {
15483     InitRND(tape.random_seed);
15484     for (i = 0; i < num_random_calls; i++)
15485       RND(1);
15486   }
15487
15488   if (game.num_random_calls != num_random_calls)
15489   {
15490     Error("number of random calls out of sync");
15491     Error("number of random calls should be %d", num_random_calls);
15492     Error("number of random calls is %d", game.num_random_calls);
15493
15494     Fail("this should not happen -- please debug");
15495   }
15496 }
15497
15498 void FreeEngineSnapshotSingle(void)
15499 {
15500   FreeSnapshotSingle();
15501
15502   setString(&snapshot_level_identifier, NULL);
15503   snapshot_level_nr = -1;
15504 }
15505
15506 void FreeEngineSnapshotList(void)
15507 {
15508   FreeSnapshotList();
15509 }
15510
15511 static ListNode *SaveEngineSnapshotBuffers(void)
15512 {
15513   ListNode *buffers = NULL;
15514
15515   // copy some special values to a structure better suited for the snapshot
15516
15517   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15518     SaveEngineSnapshotValues_RND();
15519   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15520     SaveEngineSnapshotValues_EM();
15521   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15522     SaveEngineSnapshotValues_SP(&buffers);
15523   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15524     SaveEngineSnapshotValues_MM(&buffers);
15525
15526   // save values stored in special snapshot structure
15527
15528   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15529     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15530   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15531     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15532   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15533     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15534   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15535     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15536
15537   // save further RND engine values
15538
15539   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15540   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15541   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15542
15543   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15544   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15545   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15546   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15547   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15548
15549   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15550   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15551   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15552
15553   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15554
15555   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15556   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15557
15558   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15559   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15560   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15561   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15562   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15563   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15564   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15565   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15566   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15567   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15568   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15569   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15570   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15571   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15572   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15573   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15574   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15575   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15576
15577   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15578   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15579
15580   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15581   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15582   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15583
15584   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15585   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15586
15587   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15588   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15589   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15590   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15591   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15592
15593   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15594   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15595
15596 #if 0
15597   ListNode *node = engine_snapshot_list_rnd;
15598   int num_bytes = 0;
15599
15600   while (node != NULL)
15601   {
15602     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15603
15604     node = node->next;
15605   }
15606
15607   Debug("game:playing:SaveEngineSnapshotBuffers",
15608         "size of engine snapshot: %d bytes", num_bytes);
15609 #endif
15610
15611   return buffers;
15612 }
15613
15614 void SaveEngineSnapshotSingle(void)
15615 {
15616   ListNode *buffers = SaveEngineSnapshotBuffers();
15617
15618   // finally save all snapshot buffers to single snapshot
15619   SaveSnapshotSingle(buffers);
15620
15621   // save level identification information
15622   setString(&snapshot_level_identifier, leveldir_current->identifier);
15623   snapshot_level_nr = level_nr;
15624 }
15625
15626 boolean CheckSaveEngineSnapshotToList(void)
15627 {
15628   boolean save_snapshot =
15629     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15630      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15631       game.snapshot.changed_action) ||
15632      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15633       game.snapshot.collected_item));
15634
15635   game.snapshot.changed_action = FALSE;
15636   game.snapshot.collected_item = FALSE;
15637   game.snapshot.save_snapshot = save_snapshot;
15638
15639   return save_snapshot;
15640 }
15641
15642 void SaveEngineSnapshotToList(void)
15643 {
15644   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15645       tape.quick_resume)
15646     return;
15647
15648   ListNode *buffers = SaveEngineSnapshotBuffers();
15649
15650   // finally save all snapshot buffers to snapshot list
15651   SaveSnapshotToList(buffers);
15652 }
15653
15654 void SaveEngineSnapshotToListInitial(void)
15655 {
15656   FreeEngineSnapshotList();
15657
15658   SaveEngineSnapshotToList();
15659 }
15660
15661 static void LoadEngineSnapshotValues(void)
15662 {
15663   // restore special values from snapshot structure
15664
15665   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15666     LoadEngineSnapshotValues_RND();
15667   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15668     LoadEngineSnapshotValues_EM();
15669   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15670     LoadEngineSnapshotValues_SP();
15671   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15672     LoadEngineSnapshotValues_MM();
15673 }
15674
15675 void LoadEngineSnapshotSingle(void)
15676 {
15677   LoadSnapshotSingle();
15678
15679   LoadEngineSnapshotValues();
15680 }
15681
15682 static void LoadEngineSnapshot_Undo(int steps)
15683 {
15684   LoadSnapshotFromList_Older(steps);
15685
15686   LoadEngineSnapshotValues();
15687 }
15688
15689 static void LoadEngineSnapshot_Redo(int steps)
15690 {
15691   LoadSnapshotFromList_Newer(steps);
15692
15693   LoadEngineSnapshotValues();
15694 }
15695
15696 boolean CheckEngineSnapshotSingle(void)
15697 {
15698   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15699           snapshot_level_nr == level_nr);
15700 }
15701
15702 boolean CheckEngineSnapshotList(void)
15703 {
15704   return CheckSnapshotList();
15705 }
15706
15707
15708 // ---------- new game button stuff -------------------------------------------
15709
15710 static struct
15711 {
15712   int graphic;
15713   struct XY *pos;
15714   int gadget_id;
15715   boolean *setup_value;
15716   boolean allowed_on_tape;
15717   boolean is_touch_button;
15718   char *infotext;
15719 } gamebutton_info[NUM_GAME_BUTTONS] =
15720 {
15721   {
15722     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15723     GAME_CTRL_ID_STOP,                          NULL,
15724     TRUE, FALSE,                                "stop game"
15725   },
15726   {
15727     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15728     GAME_CTRL_ID_PAUSE,                         NULL,
15729     TRUE, FALSE,                                "pause game"
15730   },
15731   {
15732     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15733     GAME_CTRL_ID_PLAY,                          NULL,
15734     TRUE, FALSE,                                "play game"
15735   },
15736   {
15737     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15738     GAME_CTRL_ID_UNDO,                          NULL,
15739     TRUE, FALSE,                                "undo step"
15740   },
15741   {
15742     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15743     GAME_CTRL_ID_REDO,                          NULL,
15744     TRUE, FALSE,                                "redo step"
15745   },
15746   {
15747     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15748     GAME_CTRL_ID_SAVE,                          NULL,
15749     TRUE, FALSE,                                "save game"
15750   },
15751   {
15752     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15753     GAME_CTRL_ID_PAUSE2,                        NULL,
15754     TRUE, FALSE,                                "pause game"
15755   },
15756   {
15757     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15758     GAME_CTRL_ID_LOAD,                          NULL,
15759     TRUE, FALSE,                                "load game"
15760   },
15761   {
15762     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15763     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15764     FALSE, FALSE,                               "stop game"
15765   },
15766   {
15767     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15768     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15769     FALSE, FALSE,                               "pause game"
15770   },
15771   {
15772     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15773     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15774     FALSE, FALSE,                               "play game"
15775   },
15776   {
15777     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15778     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15779     FALSE, TRUE,                                "stop game"
15780   },
15781   {
15782     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15783     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15784     FALSE, TRUE,                                "pause game"
15785   },
15786   {
15787     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15788     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15789     TRUE, FALSE,                                "background music on/off"
15790   },
15791   {
15792     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15793     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15794     TRUE, FALSE,                                "sound loops on/off"
15795   },
15796   {
15797     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15798     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15799     TRUE, FALSE,                                "normal sounds on/off"
15800   },
15801   {
15802     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15803     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15804     FALSE, FALSE,                               "background music on/off"
15805   },
15806   {
15807     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15808     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15809     FALSE, FALSE,                               "sound loops on/off"
15810   },
15811   {
15812     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15813     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15814     FALSE, FALSE,                               "normal sounds on/off"
15815   }
15816 };
15817
15818 void CreateGameButtons(void)
15819 {
15820   int i;
15821
15822   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15823   {
15824     int graphic = gamebutton_info[i].graphic;
15825     struct GraphicInfo *gfx = &graphic_info[graphic];
15826     struct XY *pos = gamebutton_info[i].pos;
15827     struct GadgetInfo *gi;
15828     int button_type;
15829     boolean checked;
15830     unsigned int event_mask;
15831     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15832     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15833     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15834     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15835     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15836     int gd_x   = gfx->src_x;
15837     int gd_y   = gfx->src_y;
15838     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15839     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15840     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15841     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15842     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15843     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15844     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15845     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15846     int id = i;
15847
15848     if (gfx->bitmap == NULL)
15849     {
15850       game_gadget[id] = NULL;
15851
15852       continue;
15853     }
15854
15855     if (id == GAME_CTRL_ID_STOP ||
15856         id == GAME_CTRL_ID_PANEL_STOP ||
15857         id == GAME_CTRL_ID_TOUCH_STOP ||
15858         id == GAME_CTRL_ID_PLAY ||
15859         id == GAME_CTRL_ID_PANEL_PLAY ||
15860         id == GAME_CTRL_ID_SAVE ||
15861         id == GAME_CTRL_ID_LOAD)
15862     {
15863       button_type = GD_TYPE_NORMAL_BUTTON;
15864       checked = FALSE;
15865       event_mask = GD_EVENT_RELEASED;
15866     }
15867     else if (id == GAME_CTRL_ID_UNDO ||
15868              id == GAME_CTRL_ID_REDO)
15869     {
15870       button_type = GD_TYPE_NORMAL_BUTTON;
15871       checked = FALSE;
15872       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15873     }
15874     else
15875     {
15876       button_type = GD_TYPE_CHECK_BUTTON;
15877       checked = (gamebutton_info[i].setup_value != NULL ?
15878                  *gamebutton_info[i].setup_value : FALSE);
15879       event_mask = GD_EVENT_PRESSED;
15880     }
15881
15882     gi = CreateGadget(GDI_CUSTOM_ID, id,
15883                       GDI_IMAGE_ID, graphic,
15884                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15885                       GDI_X, base_x + x,
15886                       GDI_Y, base_y + y,
15887                       GDI_WIDTH, gfx->width,
15888                       GDI_HEIGHT, gfx->height,
15889                       GDI_TYPE, button_type,
15890                       GDI_STATE, GD_BUTTON_UNPRESSED,
15891                       GDI_CHECKED, checked,
15892                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15893                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15894                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15895                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15896                       GDI_DIRECT_DRAW, FALSE,
15897                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15898                       GDI_EVENT_MASK, event_mask,
15899                       GDI_CALLBACK_ACTION, HandleGameButtons,
15900                       GDI_END);
15901
15902     if (gi == NULL)
15903       Fail("cannot create gadget");
15904
15905     game_gadget[id] = gi;
15906   }
15907 }
15908
15909 void FreeGameButtons(void)
15910 {
15911   int i;
15912
15913   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15914     FreeGadget(game_gadget[i]);
15915 }
15916
15917 static void UnmapGameButtonsAtSamePosition(int id)
15918 {
15919   int i;
15920
15921   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15922     if (i != id &&
15923         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15924         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15925       UnmapGadget(game_gadget[i]);
15926 }
15927
15928 static void UnmapGameButtonsAtSamePosition_All(void)
15929 {
15930   if (setup.show_snapshot_buttons)
15931   {
15932     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15933     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15934     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15935   }
15936   else
15937   {
15938     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15939     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15940     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15941
15942     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15943     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15944     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15945   }
15946 }
15947
15948 static void MapGameButtonsAtSamePosition(int id)
15949 {
15950   int i;
15951
15952   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15953     if (i != id &&
15954         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15955         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15956       MapGadget(game_gadget[i]);
15957
15958   UnmapGameButtonsAtSamePosition_All();
15959 }
15960
15961 void MapUndoRedoButtons(void)
15962 {
15963   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15964   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15965
15966   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15967   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15968 }
15969
15970 void UnmapUndoRedoButtons(void)
15971 {
15972   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15973   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15974
15975   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15976   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15977 }
15978
15979 void ModifyPauseButtons(void)
15980 {
15981   static int ids[] =
15982   {
15983     GAME_CTRL_ID_PAUSE,
15984     GAME_CTRL_ID_PAUSE2,
15985     GAME_CTRL_ID_PANEL_PAUSE,
15986     GAME_CTRL_ID_TOUCH_PAUSE,
15987     -1
15988   };
15989   int i;
15990
15991   for (i = 0; ids[i] > -1; i++)
15992     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15993 }
15994
15995 static void MapGameButtonsExt(boolean on_tape)
15996 {
15997   int i;
15998
15999   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16000     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16001         i != GAME_CTRL_ID_UNDO &&
16002         i != GAME_CTRL_ID_REDO)
16003       MapGadget(game_gadget[i]);
16004
16005   UnmapGameButtonsAtSamePosition_All();
16006
16007   RedrawGameButtons();
16008 }
16009
16010 static void UnmapGameButtonsExt(boolean on_tape)
16011 {
16012   int i;
16013
16014   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16015     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16016       UnmapGadget(game_gadget[i]);
16017 }
16018
16019 static void RedrawGameButtonsExt(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       RedrawGadget(game_gadget[i]);
16026 }
16027
16028 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16029 {
16030   if (gi == NULL)
16031     return;
16032
16033   gi->checked = state;
16034 }
16035
16036 static void RedrawSoundButtonGadget(int id)
16037 {
16038   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16039              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16040              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16041              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16042              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16043              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16044              id);
16045
16046   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16047   RedrawGadget(game_gadget[id2]);
16048 }
16049
16050 void MapGameButtons(void)
16051 {
16052   MapGameButtonsExt(FALSE);
16053 }
16054
16055 void UnmapGameButtons(void)
16056 {
16057   UnmapGameButtonsExt(FALSE);
16058 }
16059
16060 void RedrawGameButtons(void)
16061 {
16062   RedrawGameButtonsExt(FALSE);
16063 }
16064
16065 void MapGameButtonsOnTape(void)
16066 {
16067   MapGameButtonsExt(TRUE);
16068 }
16069
16070 void UnmapGameButtonsOnTape(void)
16071 {
16072   UnmapGameButtonsExt(TRUE);
16073 }
16074
16075 void RedrawGameButtonsOnTape(void)
16076 {
16077   RedrawGameButtonsExt(TRUE);
16078 }
16079
16080 static void GameUndoRedoExt(void)
16081 {
16082   ClearPlayerAction();
16083
16084   tape.pausing = TRUE;
16085
16086   RedrawPlayfield();
16087   UpdateAndDisplayGameControlValues();
16088
16089   DrawCompleteVideoDisplay();
16090   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16091   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16092   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16093
16094   BackToFront();
16095 }
16096
16097 static void GameUndo(int steps)
16098 {
16099   if (!CheckEngineSnapshotList())
16100     return;
16101
16102   LoadEngineSnapshot_Undo(steps);
16103
16104   GameUndoRedoExt();
16105 }
16106
16107 static void GameRedo(int steps)
16108 {
16109   if (!CheckEngineSnapshotList())
16110     return;
16111
16112   LoadEngineSnapshot_Redo(steps);
16113
16114   GameUndoRedoExt();
16115 }
16116
16117 static void HandleGameButtonsExt(int id, int button)
16118 {
16119   static boolean game_undo_executed = FALSE;
16120   int steps = BUTTON_STEPSIZE(button);
16121   boolean handle_game_buttons =
16122     (game_status == GAME_MODE_PLAYING ||
16123      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16124
16125   if (!handle_game_buttons)
16126     return;
16127
16128   switch (id)
16129   {
16130     case GAME_CTRL_ID_STOP:
16131     case GAME_CTRL_ID_PANEL_STOP:
16132     case GAME_CTRL_ID_TOUCH_STOP:
16133       if (game_status == GAME_MODE_MAIN)
16134         break;
16135
16136       if (tape.playing)
16137         TapeStop();
16138       else
16139         RequestQuitGame(TRUE);
16140
16141       break;
16142
16143     case GAME_CTRL_ID_PAUSE:
16144     case GAME_CTRL_ID_PAUSE2:
16145     case GAME_CTRL_ID_PANEL_PAUSE:
16146     case GAME_CTRL_ID_TOUCH_PAUSE:
16147       if (network.enabled && game_status == GAME_MODE_PLAYING)
16148       {
16149         if (tape.pausing)
16150           SendToServer_ContinuePlaying();
16151         else
16152           SendToServer_PausePlaying();
16153       }
16154       else
16155         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16156
16157       game_undo_executed = FALSE;
16158
16159       break;
16160
16161     case GAME_CTRL_ID_PLAY:
16162     case GAME_CTRL_ID_PANEL_PLAY:
16163       if (game_status == GAME_MODE_MAIN)
16164       {
16165         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16166       }
16167       else if (tape.pausing)
16168       {
16169         if (network.enabled)
16170           SendToServer_ContinuePlaying();
16171         else
16172           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16173       }
16174       break;
16175
16176     case GAME_CTRL_ID_UNDO:
16177       // Important: When using "save snapshot when collecting an item" mode,
16178       // load last (current) snapshot for first "undo" after pressing "pause"
16179       // (else the last-but-one snapshot would be loaded, because the snapshot
16180       // pointer already points to the last snapshot when pressing "pause",
16181       // which is fine for "every step/move" mode, but not for "every collect")
16182       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16183           !game_undo_executed)
16184         steps--;
16185
16186       game_undo_executed = TRUE;
16187
16188       GameUndo(steps);
16189       break;
16190
16191     case GAME_CTRL_ID_REDO:
16192       GameRedo(steps);
16193       break;
16194
16195     case GAME_CTRL_ID_SAVE:
16196       TapeQuickSave();
16197       break;
16198
16199     case GAME_CTRL_ID_LOAD:
16200       TapeQuickLoad();
16201       break;
16202
16203     case SOUND_CTRL_ID_MUSIC:
16204     case SOUND_CTRL_ID_PANEL_MUSIC:
16205       if (setup.sound_music)
16206       { 
16207         setup.sound_music = FALSE;
16208
16209         FadeMusic();
16210       }
16211       else if (audio.music_available)
16212       { 
16213         setup.sound = setup.sound_music = TRUE;
16214
16215         SetAudioMode(setup.sound);
16216
16217         if (game_status == GAME_MODE_PLAYING)
16218           PlayLevelMusic();
16219       }
16220
16221       RedrawSoundButtonGadget(id);
16222
16223       break;
16224
16225     case SOUND_CTRL_ID_LOOPS:
16226     case SOUND_CTRL_ID_PANEL_LOOPS:
16227       if (setup.sound_loops)
16228         setup.sound_loops = FALSE;
16229       else if (audio.loops_available)
16230       {
16231         setup.sound = setup.sound_loops = TRUE;
16232
16233         SetAudioMode(setup.sound);
16234       }
16235
16236       RedrawSoundButtonGadget(id);
16237
16238       break;
16239
16240     case SOUND_CTRL_ID_SIMPLE:
16241     case SOUND_CTRL_ID_PANEL_SIMPLE:
16242       if (setup.sound_simple)
16243         setup.sound_simple = FALSE;
16244       else if (audio.sound_available)
16245       {
16246         setup.sound = setup.sound_simple = TRUE;
16247
16248         SetAudioMode(setup.sound);
16249       }
16250
16251       RedrawSoundButtonGadget(id);
16252
16253       break;
16254
16255     default:
16256       break;
16257   }
16258 }
16259
16260 static void HandleGameButtons(struct GadgetInfo *gi)
16261 {
16262   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16263 }
16264
16265 void HandleSoundButtonKeys(Key key)
16266 {
16267   if (key == setup.shortcut.sound_simple)
16268     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16269   else if (key == setup.shortcut.sound_loops)
16270     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16271   else if (key == setup.shortcut.sound_music)
16272     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16273 }