2ccc79cd1ca3c16341cfdf204b3a052249ba2e1c
[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 void TestFieldAfterSnapping(int, int, int, int, int);
1123
1124 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1125
1126 // for detection of endless loops, caused by custom element programming
1127 // (using maximal playfield width x 10 is just a rough approximation)
1128 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1129
1130 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1131 {                                                                       \
1132   if (recursion_loop_detected)                                          \
1133     return (rc);                                                        \
1134                                                                         \
1135   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1136   {                                                                     \
1137     recursion_loop_detected = TRUE;                                     \
1138     recursion_loop_element = (e);                                       \
1139   }                                                                     \
1140                                                                         \
1141   recursion_loop_depth++;                                               \
1142 }
1143
1144 #define RECURSION_LOOP_DETECTION_END()                                  \
1145 {                                                                       \
1146   recursion_loop_depth--;                                               \
1147 }
1148
1149 static int recursion_loop_depth;
1150 static boolean recursion_loop_detected;
1151 static boolean recursion_loop_element;
1152
1153 static int map_player_action[MAX_PLAYERS];
1154
1155
1156 // ----------------------------------------------------------------------------
1157 // definition of elements that automatically change to other elements after
1158 // a specified time, eventually calling a function when changing
1159 // ----------------------------------------------------------------------------
1160
1161 // forward declaration for changer functions
1162 static void InitBuggyBase(int, int);
1163 static void WarnBuggyBase(int, int);
1164
1165 static void InitTrap(int, int);
1166 static void ActivateTrap(int, int);
1167 static void ChangeActiveTrap(int, int);
1168
1169 static void InitRobotWheel(int, int);
1170 static void RunRobotWheel(int, int);
1171 static void StopRobotWheel(int, int);
1172
1173 static void InitTimegateWheel(int, int);
1174 static void RunTimegateWheel(int, int);
1175
1176 static void InitMagicBallDelay(int, int);
1177 static void ActivateMagicBall(int, int);
1178
1179 struct ChangingElementInfo
1180 {
1181   int element;
1182   int target_element;
1183   int change_delay;
1184   void (*pre_change_function)(int x, int y);
1185   void (*change_function)(int x, int y);
1186   void (*post_change_function)(int x, int y);
1187 };
1188
1189 static struct ChangingElementInfo change_delay_list[] =
1190 {
1191   {
1192     EL_NUT_BREAKING,
1193     EL_EMERALD,
1194     6,
1195     NULL,
1196     NULL,
1197     NULL
1198   },
1199   {
1200     EL_PEARL_BREAKING,
1201     EL_EMPTY,
1202     8,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_EXIT_OPENING,
1209     EL_EXIT_OPEN,
1210     29,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EXIT_CLOSING,
1217     EL_EXIT_CLOSED,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_STEEL_EXIT_OPENING,
1225     EL_STEEL_EXIT_OPEN,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_STEEL_EXIT_CLOSING,
1233     EL_STEEL_EXIT_CLOSED,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_EM_EXIT_OPENING,
1241     EL_EM_EXIT_OPEN,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EM_EXIT_CLOSING,
1249     EL_EMPTY,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_EM_STEEL_EXIT_OPENING,
1257     EL_EM_STEEL_EXIT_OPEN,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_EM_STEEL_EXIT_CLOSING,
1265     EL_STEELWALL,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_SP_EXIT_OPENING,
1273     EL_SP_EXIT_OPEN,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_SP_EXIT_CLOSING,
1281     EL_SP_EXIT_CLOSED,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SWITCHGATE_OPENING,
1289     EL_SWITCHGATE_OPEN,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SWITCHGATE_CLOSING,
1297     EL_SWITCHGATE_CLOSED,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_TIMEGATE_OPENING,
1305     EL_TIMEGATE_OPEN,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_TIMEGATE_CLOSING,
1313     EL_TIMEGATE_CLOSED,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319
1320   {
1321     EL_ACID_SPLASH_LEFT,
1322     EL_EMPTY,
1323     8,
1324     NULL,
1325     NULL,
1326     NULL
1327   },
1328   {
1329     EL_ACID_SPLASH_RIGHT,
1330     EL_EMPTY,
1331     8,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_SP_BUGGY_BASE,
1338     EL_SP_BUGGY_BASE_ACTIVATING,
1339     0,
1340     InitBuggyBase,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_SP_BUGGY_BASE_ACTIVATING,
1346     EL_SP_BUGGY_BASE_ACTIVE,
1347     0,
1348     InitBuggyBase,
1349     NULL,
1350     NULL
1351   },
1352   {
1353     EL_SP_BUGGY_BASE_ACTIVE,
1354     EL_SP_BUGGY_BASE,
1355     0,
1356     InitBuggyBase,
1357     WarnBuggyBase,
1358     NULL
1359   },
1360   {
1361     EL_TRAP,
1362     EL_TRAP_ACTIVE,
1363     0,
1364     InitTrap,
1365     NULL,
1366     ActivateTrap
1367   },
1368   {
1369     EL_TRAP_ACTIVE,
1370     EL_TRAP,
1371     31,
1372     NULL,
1373     ChangeActiveTrap,
1374     NULL
1375   },
1376   {
1377     EL_ROBOT_WHEEL_ACTIVE,
1378     EL_ROBOT_WHEEL,
1379     0,
1380     InitRobotWheel,
1381     RunRobotWheel,
1382     StopRobotWheel
1383   },
1384   {
1385     EL_TIMEGATE_SWITCH_ACTIVE,
1386     EL_TIMEGATE_SWITCH,
1387     0,
1388     InitTimegateWheel,
1389     RunTimegateWheel,
1390     NULL
1391   },
1392   {
1393     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1394     EL_DC_TIMEGATE_SWITCH,
1395     0,
1396     InitTimegateWheel,
1397     RunTimegateWheel,
1398     NULL
1399   },
1400   {
1401     EL_EMC_MAGIC_BALL_ACTIVE,
1402     EL_EMC_MAGIC_BALL_ACTIVE,
1403     0,
1404     InitMagicBallDelay,
1405     NULL,
1406     ActivateMagicBall
1407   },
1408   {
1409     EL_EMC_SPRING_BUMPER_ACTIVE,
1410     EL_EMC_SPRING_BUMPER,
1411     8,
1412     NULL,
1413     NULL,
1414     NULL
1415   },
1416   {
1417     EL_DIAGONAL_SHRINKING,
1418     EL_UNDEFINED,
1419     0,
1420     NULL,
1421     NULL,
1422     NULL
1423   },
1424   {
1425     EL_DIAGONAL_GROWING,
1426     EL_UNDEFINED,
1427     0,
1428     NULL,
1429     NULL,
1430     NULL,
1431   },
1432
1433   {
1434     EL_UNDEFINED,
1435     EL_UNDEFINED,
1436     -1,
1437     NULL,
1438     NULL,
1439     NULL
1440   }
1441 };
1442
1443 struct
1444 {
1445   int element;
1446   int push_delay_fixed, push_delay_random;
1447 }
1448 push_delay_list[] =
1449 {
1450   { EL_SPRING,                  0, 0 },
1451   { EL_BALLOON,                 0, 0 },
1452
1453   { EL_SOKOBAN_OBJECT,          2, 0 },
1454   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1455   { EL_SATELLITE,               2, 0 },
1456   { EL_SP_DISK_YELLOW,          2, 0 },
1457
1458   { EL_UNDEFINED,               0, 0 },
1459 };
1460
1461 struct
1462 {
1463   int element;
1464   int move_stepsize;
1465 }
1466 move_stepsize_list[] =
1467 {
1468   { EL_AMOEBA_DROP,             2 },
1469   { EL_AMOEBA_DROPPING,         2 },
1470   { EL_QUICKSAND_FILLING,       1 },
1471   { EL_QUICKSAND_EMPTYING,      1 },
1472   { EL_QUICKSAND_FAST_FILLING,  2 },
1473   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1474   { EL_MAGIC_WALL_FILLING,      2 },
1475   { EL_MAGIC_WALL_EMPTYING,     2 },
1476   { EL_BD_MAGIC_WALL_FILLING,   2 },
1477   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1478   { EL_DC_MAGIC_WALL_FILLING,   2 },
1479   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1480
1481   { EL_UNDEFINED,               0 },
1482 };
1483
1484 struct
1485 {
1486   int element;
1487   int count;
1488 }
1489 collect_count_list[] =
1490 {
1491   { EL_EMERALD,                 1 },
1492   { EL_BD_DIAMOND,              1 },
1493   { EL_EMERALD_YELLOW,          1 },
1494   { EL_EMERALD_RED,             1 },
1495   { EL_EMERALD_PURPLE,          1 },
1496   { EL_DIAMOND,                 3 },
1497   { EL_SP_INFOTRON,             1 },
1498   { EL_PEARL,                   5 },
1499   { EL_CRYSTAL,                 8 },
1500
1501   { EL_UNDEFINED,               0 },
1502 };
1503
1504 struct
1505 {
1506   int element;
1507   int direction;
1508 }
1509 access_direction_list[] =
1510 {
1511   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1513   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1514   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1515   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1516   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1517   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1518   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1519   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1520   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1521   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1522
1523   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1524   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1525   { EL_SP_PORT_UP,                                                   MV_DOWN },
1526   { EL_SP_PORT_DOWN,                                         MV_UP           },
1527   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1528   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1529   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1530   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1531   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1532   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1533   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1534   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1535   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1536   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1537   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1538   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1539   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1540   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1541   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1542
1543   { EL_UNDEFINED,                       MV_NONE                              }
1544 };
1545
1546 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1547
1548 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1549 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1550 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1551                                  IS_JUST_CHANGING(x, y))
1552
1553 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1554
1555 // static variables for playfield scan mode (scanning forward or backward)
1556 static int playfield_scan_start_x = 0;
1557 static int playfield_scan_start_y = 0;
1558 static int playfield_scan_delta_x = 1;
1559 static int playfield_scan_delta_y = 1;
1560
1561 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1562                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1563                                      (y) += playfield_scan_delta_y)     \
1564                                 for ((x) = playfield_scan_start_x;      \
1565                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1566                                      (x) += playfield_scan_delta_x)
1567
1568 #ifdef DEBUG
1569 void DEBUG_SetMaximumDynamite(void)
1570 {
1571   int i;
1572
1573   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1574     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1575       local_player->inventory_element[local_player->inventory_size++] =
1576         EL_DYNAMITE;
1577 }
1578 #endif
1579
1580 static void InitPlayfieldScanModeVars(void)
1581 {
1582   if (game.use_reverse_scan_direction)
1583   {
1584     playfield_scan_start_x = lev_fieldx - 1;
1585     playfield_scan_start_y = lev_fieldy - 1;
1586
1587     playfield_scan_delta_x = -1;
1588     playfield_scan_delta_y = -1;
1589   }
1590   else
1591   {
1592     playfield_scan_start_x = 0;
1593     playfield_scan_start_y = 0;
1594
1595     playfield_scan_delta_x = 1;
1596     playfield_scan_delta_y = 1;
1597   }
1598 }
1599
1600 static void InitPlayfieldScanMode(int mode)
1601 {
1602   game.use_reverse_scan_direction =
1603     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1604
1605   InitPlayfieldScanModeVars();
1606 }
1607
1608 static int get_move_delay_from_stepsize(int move_stepsize)
1609 {
1610   move_stepsize =
1611     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1612
1613   // make sure that stepsize value is always a power of 2
1614   move_stepsize = (1 << log_2(move_stepsize));
1615
1616   return TILEX / move_stepsize;
1617 }
1618
1619 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1620                                boolean init_game)
1621 {
1622   int player_nr = player->index_nr;
1623   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1624   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1625
1626   // do no immediately change move delay -- the player might just be moving
1627   player->move_delay_value_next = move_delay;
1628
1629   // information if player can move must be set separately
1630   player->cannot_move = cannot_move;
1631
1632   if (init_game)
1633   {
1634     player->move_delay       = game.initial_move_delay[player_nr];
1635     player->move_delay_value = game.initial_move_delay_value[player_nr];
1636
1637     player->move_delay_value_next = -1;
1638
1639     player->move_delay_reset_counter = 0;
1640   }
1641 }
1642
1643 void GetPlayerConfig(void)
1644 {
1645   GameFrameDelay = setup.game_frame_delay;
1646
1647   if (!audio.sound_available)
1648     setup.sound_simple = FALSE;
1649
1650   if (!audio.loops_available)
1651     setup.sound_loops = FALSE;
1652
1653   if (!audio.music_available)
1654     setup.sound_music = FALSE;
1655
1656   if (!video.fullscreen_available)
1657     setup.fullscreen = FALSE;
1658
1659   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1660
1661   SetAudioMode(setup.sound);
1662 }
1663
1664 int GetElementFromGroupElement(int element)
1665 {
1666   if (IS_GROUP_ELEMENT(element))
1667   {
1668     struct ElementGroupInfo *group = element_info[element].group;
1669     int last_anim_random_frame = gfx.anim_random_frame;
1670     int element_pos;
1671
1672     if (group->choice_mode == ANIM_RANDOM)
1673       gfx.anim_random_frame = RND(group->num_elements_resolved);
1674
1675     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1676                                     group->choice_mode, 0,
1677                                     group->choice_pos);
1678
1679     if (group->choice_mode == ANIM_RANDOM)
1680       gfx.anim_random_frame = last_anim_random_frame;
1681
1682     group->choice_pos++;
1683
1684     element = group->element_resolved[element_pos];
1685   }
1686
1687   return element;
1688 }
1689
1690 static void IncrementSokobanFieldsNeeded(void)
1691 {
1692   if (level.sb_fields_needed)
1693     game.sokoban_fields_still_needed++;
1694 }
1695
1696 static void IncrementSokobanObjectsNeeded(void)
1697 {
1698   if (level.sb_objects_needed)
1699     game.sokoban_objects_still_needed++;
1700 }
1701
1702 static void DecrementSokobanFieldsNeeded(void)
1703 {
1704   if (game.sokoban_fields_still_needed > 0)
1705     game.sokoban_fields_still_needed--;
1706 }
1707
1708 static void DecrementSokobanObjectsNeeded(void)
1709 {
1710   if (game.sokoban_objects_still_needed > 0)
1711     game.sokoban_objects_still_needed--;
1712 }
1713
1714 static void InitPlayerField(int x, int y, int element, boolean init_game)
1715 {
1716   if (element == EL_SP_MURPHY)
1717   {
1718     if (init_game)
1719     {
1720       if (stored_player[0].present)
1721       {
1722         Tile[x][y] = EL_SP_MURPHY_CLONE;
1723
1724         return;
1725       }
1726       else
1727       {
1728         stored_player[0].initial_element = element;
1729         stored_player[0].use_murphy = TRUE;
1730
1731         if (!level.use_artwork_element[0])
1732           stored_player[0].artwork_element = EL_SP_MURPHY;
1733       }
1734
1735       Tile[x][y] = EL_PLAYER_1;
1736     }
1737   }
1738
1739   if (init_game)
1740   {
1741     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1742     int jx = player->jx, jy = player->jy;
1743
1744     player->present = TRUE;
1745
1746     player->block_last_field = (element == EL_SP_MURPHY ?
1747                                 level.sp_block_last_field :
1748                                 level.block_last_field);
1749
1750     // ---------- initialize player's last field block delay ------------------
1751
1752     // always start with reliable default value (no adjustment needed)
1753     player->block_delay_adjustment = 0;
1754
1755     // special case 1: in Supaplex, Murphy blocks last field one more frame
1756     if (player->block_last_field && element == EL_SP_MURPHY)
1757       player->block_delay_adjustment = 1;
1758
1759     // special case 2: in game engines before 3.1.1, blocking was different
1760     if (game.use_block_last_field_bug)
1761       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1762
1763     if (!network.enabled || player->connected_network)
1764     {
1765       player->active = TRUE;
1766
1767       // remove potentially duplicate players
1768       if (StorePlayer[jx][jy] == Tile[x][y])
1769         StorePlayer[jx][jy] = 0;
1770
1771       StorePlayer[x][y] = Tile[x][y];
1772
1773 #if DEBUG_INIT_PLAYER
1774       Debug("game:init:player", "- player element %d activated",
1775             player->element_nr);
1776       Debug("game:init:player", "  (local player is %d and currently %s)",
1777             local_player->element_nr,
1778             local_player->active ? "active" : "not active");
1779     }
1780 #endif
1781
1782     Tile[x][y] = EL_EMPTY;
1783
1784     player->jx = player->last_jx = x;
1785     player->jy = player->last_jy = y;
1786   }
1787
1788   // always check if player was just killed and should be reanimated
1789   {
1790     int player_nr = GET_PLAYER_NR(element);
1791     struct PlayerInfo *player = &stored_player[player_nr];
1792
1793     if (player->active && player->killed)
1794       player->reanimated = TRUE; // if player was just killed, reanimate him
1795   }
1796 }
1797
1798 static void InitField(int x, int y, boolean init_game)
1799 {
1800   int element = Tile[x][y];
1801
1802   switch (element)
1803   {
1804     case EL_SP_MURPHY:
1805     case EL_PLAYER_1:
1806     case EL_PLAYER_2:
1807     case EL_PLAYER_3:
1808     case EL_PLAYER_4:
1809       InitPlayerField(x, y, element, init_game);
1810       break;
1811
1812     case EL_SOKOBAN_FIELD_PLAYER:
1813       element = Tile[x][y] = EL_PLAYER_1;
1814       InitField(x, y, init_game);
1815
1816       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1817       InitField(x, y, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_EMPTY:
1821       IncrementSokobanFieldsNeeded();
1822       break;
1823
1824     case EL_SOKOBAN_OBJECT:
1825       IncrementSokobanObjectsNeeded();
1826       break;
1827
1828     case EL_STONEBLOCK:
1829       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1830         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1831       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1832         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1833       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1834         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1835       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1836         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1837       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1838         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1839       break;
1840
1841     case EL_BUG:
1842     case EL_BUG_RIGHT:
1843     case EL_BUG_UP:
1844     case EL_BUG_LEFT:
1845     case EL_BUG_DOWN:
1846     case EL_SPACESHIP:
1847     case EL_SPACESHIP_RIGHT:
1848     case EL_SPACESHIP_UP:
1849     case EL_SPACESHIP_LEFT:
1850     case EL_SPACESHIP_DOWN:
1851     case EL_BD_BUTTERFLY:
1852     case EL_BD_BUTTERFLY_RIGHT:
1853     case EL_BD_BUTTERFLY_UP:
1854     case EL_BD_BUTTERFLY_LEFT:
1855     case EL_BD_BUTTERFLY_DOWN:
1856     case EL_BD_FIREFLY:
1857     case EL_BD_FIREFLY_RIGHT:
1858     case EL_BD_FIREFLY_UP:
1859     case EL_BD_FIREFLY_LEFT:
1860     case EL_BD_FIREFLY_DOWN:
1861     case EL_PACMAN_RIGHT:
1862     case EL_PACMAN_UP:
1863     case EL_PACMAN_LEFT:
1864     case EL_PACMAN_DOWN:
1865     case EL_YAMYAM:
1866     case EL_YAMYAM_LEFT:
1867     case EL_YAMYAM_RIGHT:
1868     case EL_YAMYAM_UP:
1869     case EL_YAMYAM_DOWN:
1870     case EL_DARK_YAMYAM:
1871     case EL_ROBOT:
1872     case EL_PACMAN:
1873     case EL_SP_SNIKSNAK:
1874     case EL_SP_ELECTRON:
1875     case EL_MOLE:
1876     case EL_MOLE_LEFT:
1877     case EL_MOLE_RIGHT:
1878     case EL_MOLE_UP:
1879     case EL_MOLE_DOWN:
1880     case EL_SPRING_LEFT:
1881     case EL_SPRING_RIGHT:
1882       InitMovDir(x, y);
1883       break;
1884
1885     case EL_AMOEBA_FULL:
1886     case EL_BD_AMOEBA:
1887       InitAmoebaNr(x, y);
1888       break;
1889
1890     case EL_AMOEBA_DROP:
1891       if (y == lev_fieldy - 1)
1892       {
1893         Tile[x][y] = EL_AMOEBA_GROWING;
1894         Store[x][y] = EL_AMOEBA_WET;
1895       }
1896       break;
1897
1898     case EL_DYNAMITE_ACTIVE:
1899     case EL_SP_DISK_RED_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1902     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1903     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1904       MovDelay[x][y] = 96;
1905       break;
1906
1907     case EL_EM_DYNAMITE_ACTIVE:
1908       MovDelay[x][y] = 32;
1909       break;
1910
1911     case EL_LAMP:
1912       game.lights_still_needed++;
1913       break;
1914
1915     case EL_PENGUIN:
1916       game.friends_still_needed++;
1917       break;
1918
1919     case EL_PIG:
1920     case EL_DRAGON:
1921       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1922       break;
1923
1924     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1925     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1926     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1927     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1928     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1929     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1930     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1931     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1932     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1933     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1934     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1935     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1936       if (init_game)
1937       {
1938         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1939         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1940         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1941
1942         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1943         {
1944           game.belt_dir[belt_nr] = belt_dir;
1945           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1946         }
1947         else    // more than one switch -- set it like the first switch
1948         {
1949           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1950         }
1951       }
1952       break;
1953
1954     case EL_LIGHT_SWITCH_ACTIVE:
1955       if (init_game)
1956         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1957       break;
1958
1959     case EL_INVISIBLE_STEELWALL:
1960     case EL_INVISIBLE_WALL:
1961     case EL_INVISIBLE_SAND:
1962       if (game.light_time_left > 0 ||
1963           game.lenses_time_left > 0)
1964         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1965       break;
1966
1967     case EL_EMC_MAGIC_BALL:
1968       if (game.ball_active)
1969         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1970       break;
1971
1972     case EL_EMC_MAGIC_BALL_SWITCH:
1973       if (game.ball_active)
1974         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1975       break;
1976
1977     case EL_TRIGGER_PLAYER:
1978     case EL_TRIGGER_ELEMENT:
1979     case EL_TRIGGER_CE_VALUE:
1980     case EL_TRIGGER_CE_SCORE:
1981     case EL_SELF:
1982     case EL_ANY_ELEMENT:
1983     case EL_CURRENT_CE_VALUE:
1984     case EL_CURRENT_CE_SCORE:
1985     case EL_PREV_CE_1:
1986     case EL_PREV_CE_2:
1987     case EL_PREV_CE_3:
1988     case EL_PREV_CE_4:
1989     case EL_PREV_CE_5:
1990     case EL_PREV_CE_6:
1991     case EL_PREV_CE_7:
1992     case EL_PREV_CE_8:
1993     case EL_NEXT_CE_1:
1994     case EL_NEXT_CE_2:
1995     case EL_NEXT_CE_3:
1996     case EL_NEXT_CE_4:
1997     case EL_NEXT_CE_5:
1998     case EL_NEXT_CE_6:
1999     case EL_NEXT_CE_7:
2000     case EL_NEXT_CE_8:
2001       // reference elements should not be used on the playfield
2002       Tile[x][y] = EL_EMPTY;
2003       break;
2004
2005     default:
2006       if (IS_CUSTOM_ELEMENT(element))
2007       {
2008         if (CAN_MOVE(element))
2009           InitMovDir(x, y);
2010
2011         if (!element_info[element].use_last_ce_value || init_game)
2012           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2013       }
2014       else if (IS_GROUP_ELEMENT(element))
2015       {
2016         Tile[x][y] = GetElementFromGroupElement(element);
2017
2018         InitField(x, y, init_game);
2019       }
2020
2021       break;
2022   }
2023
2024   if (!init_game)
2025     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2026 }
2027
2028 static void InitField_WithBug1(int x, int y, boolean init_game)
2029 {
2030   InitField(x, y, init_game);
2031
2032   // not needed to call InitMovDir() -- already done by InitField()!
2033   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2034       CAN_MOVE(Tile[x][y]))
2035     InitMovDir(x, y);
2036 }
2037
2038 static void InitField_WithBug2(int x, int y, boolean init_game)
2039 {
2040   int old_element = Tile[x][y];
2041
2042   InitField(x, y, init_game);
2043
2044   // not needed to call InitMovDir() -- already done by InitField()!
2045   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2046       CAN_MOVE(old_element) &&
2047       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2048     InitMovDir(x, y);
2049
2050   /* this case is in fact a combination of not less than three bugs:
2051      first, it calls InitMovDir() for elements that can move, although this is
2052      already done by InitField(); then, it checks the element that was at this
2053      field _before_ the call to InitField() (which can change it); lastly, it
2054      was not called for "mole with direction" elements, which were treated as
2055      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2056   */
2057 }
2058
2059 static int get_key_element_from_nr(int key_nr)
2060 {
2061   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2062                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2063                           EL_EM_KEY_1 : EL_KEY_1);
2064
2065   return key_base_element + key_nr;
2066 }
2067
2068 static int get_next_dropped_element(struct PlayerInfo *player)
2069 {
2070   return (player->inventory_size > 0 ?
2071           player->inventory_element[player->inventory_size - 1] :
2072           player->inventory_infinite_element != EL_UNDEFINED ?
2073           player->inventory_infinite_element :
2074           player->dynabombs_left > 0 ?
2075           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2076           EL_UNDEFINED);
2077 }
2078
2079 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2080 {
2081   // pos >= 0: get element from bottom of the stack;
2082   // pos <  0: get element from top of the stack
2083
2084   if (pos < 0)
2085   {
2086     int min_inventory_size = -pos;
2087     int inventory_pos = player->inventory_size - min_inventory_size;
2088     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2089
2090     return (player->inventory_size >= min_inventory_size ?
2091             player->inventory_element[inventory_pos] :
2092             player->inventory_infinite_element != EL_UNDEFINED ?
2093             player->inventory_infinite_element :
2094             player->dynabombs_left >= min_dynabombs_left ?
2095             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2096             EL_UNDEFINED);
2097   }
2098   else
2099   {
2100     int min_dynabombs_left = pos + 1;
2101     int min_inventory_size = pos + 1 - player->dynabombs_left;
2102     int inventory_pos = pos - player->dynabombs_left;
2103
2104     return (player->inventory_infinite_element != EL_UNDEFINED ?
2105             player->inventory_infinite_element :
2106             player->dynabombs_left >= min_dynabombs_left ?
2107             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2108             player->inventory_size >= min_inventory_size ?
2109             player->inventory_element[inventory_pos] :
2110             EL_UNDEFINED);
2111   }
2112 }
2113
2114 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2115 {
2116   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2117   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2118   int compare_result;
2119
2120   if (gpo1->sort_priority != gpo2->sort_priority)
2121     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2122   else
2123     compare_result = gpo1->nr - gpo2->nr;
2124
2125   return compare_result;
2126 }
2127
2128 int getPlayerInventorySize(int player_nr)
2129 {
2130   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2131     return game_em.ply[player_nr]->dynamite;
2132   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2133     return game_sp.red_disk_count;
2134   else
2135     return stored_player[player_nr].inventory_size;
2136 }
2137
2138 static void InitGameControlValues(void)
2139 {
2140   int i;
2141
2142   for (i = 0; game_panel_controls[i].nr != -1; i++)
2143   {
2144     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2145     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2146     struct TextPosInfo *pos = gpc->pos;
2147     int nr = gpc->nr;
2148     int type = gpc->type;
2149
2150     if (nr != i)
2151     {
2152       Error("'game_panel_controls' structure corrupted at %d", i);
2153
2154       Fail("this should not happen -- please debug");
2155     }
2156
2157     // force update of game controls after initialization
2158     gpc->value = gpc->last_value = -1;
2159     gpc->frame = gpc->last_frame = -1;
2160     gpc->gfx_frame = -1;
2161
2162     // determine panel value width for later calculation of alignment
2163     if (type == TYPE_INTEGER || type == TYPE_STRING)
2164     {
2165       pos->width = pos->size * getFontWidth(pos->font);
2166       pos->height = getFontHeight(pos->font);
2167     }
2168     else if (type == TYPE_ELEMENT)
2169     {
2170       pos->width = pos->size;
2171       pos->height = pos->size;
2172     }
2173
2174     // fill structure for game panel draw order
2175     gpo->nr = gpc->nr;
2176     gpo->sort_priority = pos->sort_priority;
2177   }
2178
2179   // sort game panel controls according to sort_priority and control number
2180   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2181         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2182 }
2183
2184 static void UpdatePlayfieldElementCount(void)
2185 {
2186   boolean use_element_count = FALSE;
2187   int i, j, x, y;
2188
2189   // first check if it is needed at all to calculate playfield element count
2190   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2191     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2192       use_element_count = TRUE;
2193
2194   if (!use_element_count)
2195     return;
2196
2197   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2198     element_info[i].element_count = 0;
2199
2200   SCAN_PLAYFIELD(x, y)
2201   {
2202     element_info[Tile[x][y]].element_count++;
2203   }
2204
2205   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2206     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2207       if (IS_IN_GROUP(j, i))
2208         element_info[EL_GROUP_START + i].element_count +=
2209           element_info[j].element_count;
2210 }
2211
2212 static void UpdateGameControlValues(void)
2213 {
2214   int i, k;
2215   int time = (game.LevelSolved ?
2216               game.LevelSolved_CountingTime :
2217               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2218               game_em.lev->time :
2219               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2220               game_sp.time_played :
2221               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2222               game_mm.energy_left :
2223               game.no_time_limit ? TimePlayed : TimeLeft);
2224   int score = (game.LevelSolved ?
2225                game.LevelSolved_CountingScore :
2226                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2227                game_em.lev->score :
2228                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2229                game_sp.score :
2230                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2231                game_mm.score :
2232                game.score);
2233   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2234               game_em.lev->gems_needed :
2235               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2236               game_sp.infotrons_still_needed :
2237               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2238               game_mm.kettles_still_needed :
2239               game.gems_still_needed);
2240   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2241                      game_em.lev->gems_needed > 0 :
2242                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2243                      game_sp.infotrons_still_needed > 0 :
2244                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2245                      game_mm.kettles_still_needed > 0 ||
2246                      game_mm.lights_still_needed > 0 :
2247                      game.gems_still_needed > 0 ||
2248                      game.sokoban_fields_still_needed > 0 ||
2249                      game.sokoban_objects_still_needed > 0 ||
2250                      game.lights_still_needed > 0);
2251   int health = (game.LevelSolved ?
2252                 game.LevelSolved_CountingHealth :
2253                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2254                 MM_HEALTH(game_mm.laser_overload_value) :
2255                 game.health);
2256   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2257
2258   UpdatePlayfieldElementCount();
2259
2260   // update game panel control values
2261
2262   // used instead of "level_nr" (for network games)
2263   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2264   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2265
2266   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2267   for (i = 0; i < MAX_NUM_KEYS; i++)
2268     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2269   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2270   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2271
2272   if (game.centered_player_nr == -1)
2273   {
2274     for (i = 0; i < MAX_PLAYERS; i++)
2275     {
2276       // only one player in Supaplex game engine
2277       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2278         break;
2279
2280       for (k = 0; k < MAX_NUM_KEYS; k++)
2281       {
2282         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2283         {
2284           if (game_em.ply[i]->keys & (1 << k))
2285             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2286               get_key_element_from_nr(k);
2287         }
2288         else if (stored_player[i].key[k])
2289           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2290             get_key_element_from_nr(k);
2291       }
2292
2293       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2294         getPlayerInventorySize(i);
2295
2296       if (stored_player[i].num_white_keys > 0)
2297         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2298           EL_DC_KEY_WHITE;
2299
2300       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2301         stored_player[i].num_white_keys;
2302     }
2303   }
2304   else
2305   {
2306     int player_nr = game.centered_player_nr;
2307
2308     for (k = 0; k < MAX_NUM_KEYS; k++)
2309     {
2310       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2311       {
2312         if (game_em.ply[player_nr]->keys & (1 << k))
2313           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2314             get_key_element_from_nr(k);
2315       }
2316       else if (stored_player[player_nr].key[k])
2317         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2318           get_key_element_from_nr(k);
2319     }
2320
2321     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2322       getPlayerInventorySize(player_nr);
2323
2324     if (stored_player[player_nr].num_white_keys > 0)
2325       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2326
2327     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328       stored_player[player_nr].num_white_keys;
2329   }
2330
2331   // re-arrange keys on game panel, if needed or if defined by style settings
2332   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2333   {
2334     int nr = GAME_PANEL_KEY_1 + i;
2335     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2336     struct TextPosInfo *pos = gpc->pos;
2337
2338     // skip check if key is not in the player's inventory
2339     if (gpc->value == EL_EMPTY)
2340       continue;
2341
2342     // check if keys should be arranged on panel from left to right
2343     if (pos->style == STYLE_LEFTMOST_POSITION)
2344     {
2345       // check previous key positions (left from current key)
2346       for (k = 0; k < i; k++)
2347       {
2348         int nr_new = GAME_PANEL_KEY_1 + k;
2349
2350         if (game_panel_controls[nr_new].value == EL_EMPTY)
2351         {
2352           game_panel_controls[nr_new].value = gpc->value;
2353           gpc->value = EL_EMPTY;
2354
2355           break;
2356         }
2357       }
2358     }
2359
2360     // check if "undefined" keys can be placed at some other position
2361     if (pos->x == -1 && pos->y == -1)
2362     {
2363       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2364
2365       // 1st try: display key at the same position as normal or EM keys
2366       if (game_panel_controls[nr_new].value == EL_EMPTY)
2367       {
2368         game_panel_controls[nr_new].value = gpc->value;
2369       }
2370       else
2371       {
2372         // 2nd try: display key at the next free position in the key panel
2373         for (k = 0; k < STD_NUM_KEYS; k++)
2374         {
2375           nr_new = GAME_PANEL_KEY_1 + k;
2376
2377           if (game_panel_controls[nr_new].value == EL_EMPTY)
2378           {
2379             game_panel_controls[nr_new].value = gpc->value;
2380
2381             break;
2382           }
2383         }
2384       }
2385     }
2386   }
2387
2388   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2389   {
2390     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2391       get_inventory_element_from_pos(local_player, i);
2392     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2393       get_inventory_element_from_pos(local_player, -i - 1);
2394   }
2395
2396   game_panel_controls[GAME_PANEL_SCORE].value = score;
2397   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2398
2399   game_panel_controls[GAME_PANEL_TIME].value = time;
2400
2401   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2402   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2403   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2404
2405   if (level.time == 0)
2406     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2407   else
2408     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2409
2410   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2411   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2412
2413   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2414
2415   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2416     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2417      EL_EMPTY);
2418   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2419     local_player->shield_normal_time_left;
2420   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2421     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2422      EL_EMPTY);
2423   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2424     local_player->shield_deadly_time_left;
2425
2426   game_panel_controls[GAME_PANEL_EXIT].value =
2427     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2428
2429   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2430     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2431   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2432     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2433      EL_EMC_MAGIC_BALL_SWITCH);
2434
2435   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2436     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2437   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2438     game.light_time_left;
2439
2440   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2441     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2442   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2443     game.timegate_time_left;
2444
2445   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2446     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2447
2448   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2449     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2451     game.lenses_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2454     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2455   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2456     game.magnify_time_left;
2457
2458   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2459     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2460      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2461      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2462      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2463      EL_BALLOON_SWITCH_NONE);
2464
2465   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2466     local_player->dynabomb_count;
2467   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2468     local_player->dynabomb_size;
2469   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2470     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2471
2472   game_panel_controls[GAME_PANEL_PENGUINS].value =
2473     game.friends_still_needed;
2474
2475   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2476     game.sokoban_objects_still_needed;
2477   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2478     game.sokoban_fields_still_needed;
2479
2480   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2481     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2482
2483   for (i = 0; i < NUM_BELTS; i++)
2484   {
2485     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2486       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2487        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2488     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2489       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2490   }
2491
2492   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2493     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2494   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2495     game.magic_wall_time_left;
2496
2497   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2498     local_player->gravity;
2499
2500   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2501     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2502
2503   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2504     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2505       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2506        game.panel.element[i].id : EL_UNDEFINED);
2507
2508   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2509     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2510       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2511        element_info[game.panel.element_count[i].id].element_count : 0);
2512
2513   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2514     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2515       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2516        element_info[game.panel.ce_score[i].id].collect_score : 0);
2517
2518   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2519     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2520       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2521        element_info[game.panel.ce_score_element[i].id].collect_score :
2522        EL_UNDEFINED);
2523
2524   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2525   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2526   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2527
2528   // update game panel control frames
2529
2530   for (i = 0; game_panel_controls[i].nr != -1; i++)
2531   {
2532     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2533
2534     if (gpc->type == TYPE_ELEMENT)
2535     {
2536       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2537       {
2538         int last_anim_random_frame = gfx.anim_random_frame;
2539         int element = gpc->value;
2540         int graphic = el2panelimg(element);
2541         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2542                                sync_random_frame : INIT_GFX_RANDOM());
2543
2544         if (gpc->value != gpc->last_value)
2545         {
2546           gpc->gfx_frame = 0;
2547           gpc->gfx_random = init_gfx_random;
2548         }
2549         else
2550         {
2551           gpc->gfx_frame++;
2552
2553           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2554               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2555             gpc->gfx_random = init_gfx_random;
2556         }
2557
2558         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2559           gfx.anim_random_frame = gpc->gfx_random;
2560
2561         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2562           gpc->gfx_frame = element_info[element].collect_score;
2563
2564         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2565
2566         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2567           gfx.anim_random_frame = last_anim_random_frame;
2568       }
2569     }
2570     else if (gpc->type == TYPE_GRAPHIC)
2571     {
2572       if (gpc->graphic != IMG_UNDEFINED)
2573       {
2574         int last_anim_random_frame = gfx.anim_random_frame;
2575         int graphic = gpc->graphic;
2576         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2577                                sync_random_frame : INIT_GFX_RANDOM());
2578
2579         if (gpc->value != gpc->last_value)
2580         {
2581           gpc->gfx_frame = 0;
2582           gpc->gfx_random = init_gfx_random;
2583         }
2584         else
2585         {
2586           gpc->gfx_frame++;
2587
2588           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2589               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2590             gpc->gfx_random = init_gfx_random;
2591         }
2592
2593         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2594           gfx.anim_random_frame = gpc->gfx_random;
2595
2596         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2597
2598         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2599           gfx.anim_random_frame = last_anim_random_frame;
2600       }
2601     }
2602   }
2603 }
2604
2605 static void DisplayGameControlValues(void)
2606 {
2607   boolean redraw_panel = FALSE;
2608   int i;
2609
2610   for (i = 0; game_panel_controls[i].nr != -1; i++)
2611   {
2612     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2613
2614     if (PANEL_DEACTIVATED(gpc->pos))
2615       continue;
2616
2617     if (gpc->value == gpc->last_value &&
2618         gpc->frame == gpc->last_frame)
2619       continue;
2620
2621     redraw_panel = TRUE;
2622   }
2623
2624   if (!redraw_panel)
2625     return;
2626
2627   // copy default game door content to main double buffer
2628
2629   // !!! CHECK AGAIN !!!
2630   SetPanelBackground();
2631   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2632   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2633
2634   // redraw game control buttons
2635   RedrawGameButtons();
2636
2637   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2638
2639   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2640   {
2641     int nr = game_panel_order[i].nr;
2642     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2643     struct TextPosInfo *pos = gpc->pos;
2644     int type = gpc->type;
2645     int value = gpc->value;
2646     int frame = gpc->frame;
2647     int size = pos->size;
2648     int font = pos->font;
2649     boolean draw_masked = pos->draw_masked;
2650     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2651
2652     if (PANEL_DEACTIVATED(pos))
2653       continue;
2654
2655     if (pos->class == get_hash_from_key("extra_panel_items") &&
2656         !setup.prefer_extra_panel_items)
2657       continue;
2658
2659     gpc->last_value = value;
2660     gpc->last_frame = frame;
2661
2662     if (type == TYPE_INTEGER)
2663     {
2664       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2665           nr == GAME_PANEL_TIME)
2666       {
2667         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2668
2669         if (use_dynamic_size)           // use dynamic number of digits
2670         {
2671           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2672           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2673           int size2 = size1 + 1;
2674           int font1 = pos->font;
2675           int font2 = pos->font_alt;
2676
2677           size = (value < value_change ? size1 : size2);
2678           font = (value < value_change ? font1 : font2);
2679         }
2680       }
2681
2682       // correct text size if "digits" is zero or less
2683       if (size <= 0)
2684         size = strlen(int2str(value, size));
2685
2686       // dynamically correct text alignment
2687       pos->width = size * getFontWidth(font);
2688
2689       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2690                   int2str(value, size), font, mask_mode);
2691     }
2692     else if (type == TYPE_ELEMENT)
2693     {
2694       int element, graphic;
2695       Bitmap *src_bitmap;
2696       int src_x, src_y;
2697       int width, height;
2698       int dst_x = PANEL_XPOS(pos);
2699       int dst_y = PANEL_YPOS(pos);
2700
2701       if (value != EL_UNDEFINED && value != EL_EMPTY)
2702       {
2703         element = value;
2704         graphic = el2panelimg(value);
2705
2706 #if 0
2707         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2708               element, EL_NAME(element), size);
2709 #endif
2710
2711         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2712           size = TILESIZE;
2713
2714         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2715                               &src_x, &src_y);
2716
2717         width  = graphic_info[graphic].width  * size / TILESIZE;
2718         height = graphic_info[graphic].height * size / TILESIZE;
2719
2720         if (draw_masked)
2721           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2722                            dst_x, dst_y);
2723         else
2724           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2725                      dst_x, dst_y);
2726       }
2727     }
2728     else if (type == TYPE_GRAPHIC)
2729     {
2730       int graphic        = gpc->graphic;
2731       int graphic_active = gpc->graphic_active;
2732       Bitmap *src_bitmap;
2733       int src_x, src_y;
2734       int width, height;
2735       int dst_x = PANEL_XPOS(pos);
2736       int dst_y = PANEL_YPOS(pos);
2737       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2738                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2739
2740       if (graphic != IMG_UNDEFINED && !skip)
2741       {
2742         if (pos->style == STYLE_REVERSE)
2743           value = 100 - value;
2744
2745         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2746
2747         if (pos->direction & MV_HORIZONTAL)
2748         {
2749           width  = graphic_info[graphic_active].width * value / 100;
2750           height = graphic_info[graphic_active].height;
2751
2752           if (pos->direction == MV_LEFT)
2753           {
2754             src_x += graphic_info[graphic_active].width - width;
2755             dst_x += graphic_info[graphic_active].width - width;
2756           }
2757         }
2758         else
2759         {
2760           width  = graphic_info[graphic_active].width;
2761           height = graphic_info[graphic_active].height * value / 100;
2762
2763           if (pos->direction == MV_UP)
2764           {
2765             src_y += graphic_info[graphic_active].height - height;
2766             dst_y += graphic_info[graphic_active].height - height;
2767           }
2768         }
2769
2770         if (draw_masked)
2771           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2772                            dst_x, dst_y);
2773         else
2774           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2775                      dst_x, dst_y);
2776
2777         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2778
2779         if (pos->direction & MV_HORIZONTAL)
2780         {
2781           if (pos->direction == MV_RIGHT)
2782           {
2783             src_x += width;
2784             dst_x += width;
2785           }
2786           else
2787           {
2788             dst_x = PANEL_XPOS(pos);
2789           }
2790
2791           width = graphic_info[graphic].width - width;
2792         }
2793         else
2794         {
2795           if (pos->direction == MV_DOWN)
2796           {
2797             src_y += height;
2798             dst_y += height;
2799           }
2800           else
2801           {
2802             dst_y = PANEL_YPOS(pos);
2803           }
2804
2805           height = graphic_info[graphic].height - height;
2806         }
2807
2808         if (draw_masked)
2809           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2810                            dst_x, dst_y);
2811         else
2812           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2813                      dst_x, dst_y);
2814       }
2815     }
2816     else if (type == TYPE_STRING)
2817     {
2818       boolean active = (value != 0);
2819       char *state_normal = "off";
2820       char *state_active = "on";
2821       char *state = (active ? state_active : state_normal);
2822       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2823                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2824                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2825                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2826
2827       if (nr == GAME_PANEL_GRAVITY_STATE)
2828       {
2829         int font1 = pos->font;          // (used for normal state)
2830         int font2 = pos->font_alt;      // (used for active state)
2831
2832         font = (active ? font2 : font1);
2833       }
2834
2835       if (s != NULL)
2836       {
2837         char *s_cut;
2838
2839         if (size <= 0)
2840         {
2841           // don't truncate output if "chars" is zero or less
2842           size = strlen(s);
2843
2844           // dynamically correct text alignment
2845           pos->width = size * getFontWidth(font);
2846         }
2847
2848         s_cut = getStringCopyN(s, size);
2849
2850         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2851                     s_cut, font, mask_mode);
2852
2853         free(s_cut);
2854       }
2855     }
2856
2857     redraw_mask |= REDRAW_DOOR_1;
2858   }
2859
2860   SetGameStatus(GAME_MODE_PLAYING);
2861 }
2862
2863 void UpdateAndDisplayGameControlValues(void)
2864 {
2865   if (tape.deactivate_display)
2866     return;
2867
2868   UpdateGameControlValues();
2869   DisplayGameControlValues();
2870 }
2871
2872 #if 0
2873 static void UpdateGameDoorValues(void)
2874 {
2875   UpdateGameControlValues();
2876 }
2877 #endif
2878
2879 void DrawGameDoorValues(void)
2880 {
2881   DisplayGameControlValues();
2882 }
2883
2884
2885 // ============================================================================
2886 // InitGameEngine()
2887 // ----------------------------------------------------------------------------
2888 // initialize game engine due to level / tape version number
2889 // ============================================================================
2890
2891 static void InitGameEngine(void)
2892 {
2893   int i, j, k, l, x, y;
2894
2895   // set game engine from tape file when re-playing, else from level file
2896   game.engine_version = (tape.playing ? tape.engine_version :
2897                          level.game_version);
2898
2899   // set single or multi-player game mode (needed for re-playing tapes)
2900   game.team_mode = setup.team_mode;
2901
2902   if (tape.playing)
2903   {
2904     int num_players = 0;
2905
2906     for (i = 0; i < MAX_PLAYERS; i++)
2907       if (tape.player_participates[i])
2908         num_players++;
2909
2910     // multi-player tapes contain input data for more than one player
2911     game.team_mode = (num_players > 1);
2912   }
2913
2914 #if 0
2915   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2916         level.game_version);
2917   Debug("game:init:level", "          tape.file_version   == %06d",
2918         tape.file_version);
2919   Debug("game:init:level", "          tape.game_version   == %06d",
2920         tape.game_version);
2921   Debug("game:init:level", "          tape.engine_version == %06d",
2922         tape.engine_version);
2923   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2924         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2925 #endif
2926
2927   // --------------------------------------------------------------------------
2928   // set flags for bugs and changes according to active game engine version
2929   // --------------------------------------------------------------------------
2930
2931   /*
2932     Summary of bugfix:
2933     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2934
2935     Bug was introduced in version:
2936     2.0.1
2937
2938     Bug was fixed in version:
2939     4.2.0.0
2940
2941     Description:
2942     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2943     but the property "can fall" was missing, which caused some levels to be
2944     unsolvable. This was fixed in version 4.2.0.0.
2945
2946     Affected levels/tapes:
2947     An example for a tape that was fixed by this bugfix is tape 029 from the
2948     level set "rnd_sam_bateman".
2949     The wrong behaviour will still be used for all levels or tapes that were
2950     created/recorded with it. An example for this is tape 023 from the level
2951     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2952   */
2953
2954   boolean use_amoeba_dropping_cannot_fall_bug =
2955     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2956       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2957      (tape.playing &&
2958       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2959       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2960
2961   /*
2962     Summary of bugfix/change:
2963     Fixed move speed of elements entering or leaving magic wall.
2964
2965     Fixed/changed in version:
2966     2.0.1
2967
2968     Description:
2969     Before 2.0.1, move speed of elements entering or leaving magic wall was
2970     twice as fast as it is now.
2971     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2972
2973     Affected levels/tapes:
2974     The first condition is generally needed for all levels/tapes before version
2975     2.0.1, which might use the old behaviour before it was changed; known tapes
2976     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2977     The second condition is an exception from the above case and is needed for
2978     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2979     above, but before it was known that this change would break tapes like the
2980     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2981     although the engine version while recording maybe was before 2.0.1. There
2982     are a lot of tapes that are affected by this exception, like tape 006 from
2983     the level set "rnd_conor_mancone".
2984   */
2985
2986   boolean use_old_move_stepsize_for_magic_wall =
2987     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2988      !(tape.playing &&
2989        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2990        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2991
2992   /*
2993     Summary of bugfix/change:
2994     Fixed handling for custom elements that change when pushed by the player.
2995
2996     Fixed/changed in version:
2997     3.1.0
2998
2999     Description:
3000     Before 3.1.0, custom elements that "change when pushing" changed directly
3001     after the player started pushing them (until then handled in "DigField()").
3002     Since 3.1.0, these custom elements are not changed until the "pushing"
3003     move of the element is finished (now handled in "ContinueMoving()").
3004
3005     Affected levels/tapes:
3006     The first condition is generally needed for all levels/tapes before version
3007     3.1.0, which might use the old behaviour before it was changed; known tapes
3008     that are affected are some tapes from the level set "Walpurgis Gardens" by
3009     Jamie Cullen.
3010     The second condition is an exception from the above case and is needed for
3011     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3012     above (including some development versions of 3.1.0), but before it was
3013     known that this change would break tapes like the above and was fixed in
3014     3.1.1, so that the changed behaviour was active although the engine version
3015     while recording maybe was before 3.1.0. There is at least one tape that is
3016     affected by this exception, which is the tape for the one-level set "Bug
3017     Machine" by Juergen Bonhagen.
3018   */
3019
3020   game.use_change_when_pushing_bug =
3021     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3022      !(tape.playing &&
3023        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3024        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3025
3026   /*
3027     Summary of bugfix/change:
3028     Fixed handling for blocking the field the player leaves when moving.
3029
3030     Fixed/changed in version:
3031     3.1.1
3032
3033     Description:
3034     Before 3.1.1, when "block last field when moving" was enabled, the field
3035     the player is leaving when moving was blocked for the time of the move,
3036     and was directly unblocked afterwards. This resulted in the last field
3037     being blocked for exactly one less than the number of frames of one player
3038     move. Additionally, even when blocking was disabled, the last field was
3039     blocked for exactly one frame.
3040     Since 3.1.1, due to changes in player movement handling, the last field
3041     is not blocked at all when blocking is disabled. When blocking is enabled,
3042     the last field is blocked for exactly the number of frames of one player
3043     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3044     last field is blocked for exactly one more than the number of frames of
3045     one player move.
3046
3047     Affected levels/tapes:
3048     (!!! yet to be determined -- probably many !!!)
3049   */
3050
3051   game.use_block_last_field_bug =
3052     (game.engine_version < VERSION_IDENT(3,1,1,0));
3053
3054   /* various special flags and settings for native Emerald Mine game engine */
3055
3056   game_em.use_single_button =
3057     (game.engine_version > VERSION_IDENT(4,0,0,2));
3058
3059   game_em.use_snap_key_bug =
3060     (game.engine_version < VERSION_IDENT(4,0,1,0));
3061
3062   game_em.use_random_bug =
3063     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3064
3065   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3066
3067   game_em.use_old_explosions            = use_old_em_engine;
3068   game_em.use_old_android               = use_old_em_engine;
3069   game_em.use_old_push_elements         = use_old_em_engine;
3070   game_em.use_old_push_into_acid        = use_old_em_engine;
3071
3072   game_em.use_wrap_around               = !use_old_em_engine;
3073
3074   // --------------------------------------------------------------------------
3075
3076   // set maximal allowed number of custom element changes per game frame
3077   game.max_num_changes_per_frame = 1;
3078
3079   // default scan direction: scan playfield from top/left to bottom/right
3080   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3081
3082   // dynamically adjust element properties according to game engine version
3083   InitElementPropertiesEngine(game.engine_version);
3084
3085   // ---------- initialize special element properties -------------------------
3086
3087   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3088   if (use_amoeba_dropping_cannot_fall_bug)
3089     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3090
3091   // ---------- initialize player's initial move delay ------------------------
3092
3093   // dynamically adjust player properties according to level information
3094   for (i = 0; i < MAX_PLAYERS; i++)
3095     game.initial_move_delay_value[i] =
3096       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3097
3098   // dynamically adjust player properties according to game engine version
3099   for (i = 0; i < MAX_PLAYERS; i++)
3100     game.initial_move_delay[i] =
3101       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3102        game.initial_move_delay_value[i] : 0);
3103
3104   // ---------- initialize player's initial push delay ------------------------
3105
3106   // dynamically adjust player properties according to game engine version
3107   game.initial_push_delay_value =
3108     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3109
3110   // ---------- initialize changing elements ----------------------------------
3111
3112   // initialize changing elements information
3113   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3114   {
3115     struct ElementInfo *ei = &element_info[i];
3116
3117     // this pointer might have been changed in the level editor
3118     ei->change = &ei->change_page[0];
3119
3120     if (!IS_CUSTOM_ELEMENT(i))
3121     {
3122       ei->change->target_element = EL_EMPTY_SPACE;
3123       ei->change->delay_fixed = 0;
3124       ei->change->delay_random = 0;
3125       ei->change->delay_frames = 1;
3126     }
3127
3128     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3129     {
3130       ei->has_change_event[j] = FALSE;
3131
3132       ei->event_page_nr[j] = 0;
3133       ei->event_page[j] = &ei->change_page[0];
3134     }
3135   }
3136
3137   // add changing elements from pre-defined list
3138   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3139   {
3140     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3141     struct ElementInfo *ei = &element_info[ch_delay->element];
3142
3143     ei->change->target_element       = ch_delay->target_element;
3144     ei->change->delay_fixed          = ch_delay->change_delay;
3145
3146     ei->change->pre_change_function  = ch_delay->pre_change_function;
3147     ei->change->change_function      = ch_delay->change_function;
3148     ei->change->post_change_function = ch_delay->post_change_function;
3149
3150     ei->change->can_change = TRUE;
3151     ei->change->can_change_or_has_action = TRUE;
3152
3153     ei->has_change_event[CE_DELAY] = TRUE;
3154
3155     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3156     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3157   }
3158
3159   // ---------- initialize internal run-time variables ------------------------
3160
3161   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3162   {
3163     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3164
3165     for (j = 0; j < ei->num_change_pages; j++)
3166     {
3167       ei->change_page[j].can_change_or_has_action =
3168         (ei->change_page[j].can_change |
3169          ei->change_page[j].has_action);
3170     }
3171   }
3172
3173   // add change events from custom element configuration
3174   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3175   {
3176     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3177
3178     for (j = 0; j < ei->num_change_pages; j++)
3179     {
3180       if (!ei->change_page[j].can_change_or_has_action)
3181         continue;
3182
3183       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3184       {
3185         // only add event page for the first page found with this event
3186         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3187         {
3188           ei->has_change_event[k] = TRUE;
3189
3190           ei->event_page_nr[k] = j;
3191           ei->event_page[k] = &ei->change_page[j];
3192         }
3193       }
3194     }
3195   }
3196
3197   // ---------- initialize reference elements in change conditions ------------
3198
3199   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3200   {
3201     int element = EL_CUSTOM_START + i;
3202     struct ElementInfo *ei = &element_info[element];
3203
3204     for (j = 0; j < ei->num_change_pages; j++)
3205     {
3206       int trigger_element = ei->change_page[j].initial_trigger_element;
3207
3208       if (trigger_element >= EL_PREV_CE_8 &&
3209           trigger_element <= EL_NEXT_CE_8)
3210         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3211
3212       ei->change_page[j].trigger_element = trigger_element;
3213     }
3214   }
3215
3216   // ---------- initialize run-time trigger player and element ----------------
3217
3218   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3219   {
3220     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3221
3222     for (j = 0; j < ei->num_change_pages; j++)
3223     {
3224       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3225       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3226       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3227       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3228       ei->change_page[j].actual_trigger_ce_value = 0;
3229       ei->change_page[j].actual_trigger_ce_score = 0;
3230     }
3231   }
3232
3233   // ---------- initialize trigger events -------------------------------------
3234
3235   // initialize trigger events information
3236   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3237     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3238       trigger_events[i][j] = FALSE;
3239
3240   // add trigger events from element change event properties
3241   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3242   {
3243     struct ElementInfo *ei = &element_info[i];
3244
3245     for (j = 0; j < ei->num_change_pages; j++)
3246     {
3247       if (!ei->change_page[j].can_change_or_has_action)
3248         continue;
3249
3250       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3251       {
3252         int trigger_element = ei->change_page[j].trigger_element;
3253
3254         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3255         {
3256           if (ei->change_page[j].has_event[k])
3257           {
3258             if (IS_GROUP_ELEMENT(trigger_element))
3259             {
3260               struct ElementGroupInfo *group =
3261                 element_info[trigger_element].group;
3262
3263               for (l = 0; l < group->num_elements_resolved; l++)
3264                 trigger_events[group->element_resolved[l]][k] = TRUE;
3265             }
3266             else if (trigger_element == EL_ANY_ELEMENT)
3267               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3268                 trigger_events[l][k] = TRUE;
3269             else
3270               trigger_events[trigger_element][k] = TRUE;
3271           }
3272         }
3273       }
3274     }
3275   }
3276
3277   // ---------- initialize push delay -----------------------------------------
3278
3279   // initialize push delay values to default
3280   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3281   {
3282     if (!IS_CUSTOM_ELEMENT(i))
3283     {
3284       // set default push delay values (corrected since version 3.0.7-1)
3285       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3286       {
3287         element_info[i].push_delay_fixed = 2;
3288         element_info[i].push_delay_random = 8;
3289       }
3290       else
3291       {
3292         element_info[i].push_delay_fixed = 8;
3293         element_info[i].push_delay_random = 8;
3294       }
3295     }
3296   }
3297
3298   // set push delay value for certain elements from pre-defined list
3299   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3300   {
3301     int e = push_delay_list[i].element;
3302
3303     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3304     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3305   }
3306
3307   // set push delay value for Supaplex elements for newer engine versions
3308   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3309   {
3310     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3311     {
3312       if (IS_SP_ELEMENT(i))
3313       {
3314         // set SP push delay to just enough to push under a falling zonk
3315         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3316
3317         element_info[i].push_delay_fixed  = delay;
3318         element_info[i].push_delay_random = 0;
3319       }
3320     }
3321   }
3322
3323   // ---------- initialize move stepsize --------------------------------------
3324
3325   // initialize move stepsize values to default
3326   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3327     if (!IS_CUSTOM_ELEMENT(i))
3328       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3329
3330   // set move stepsize value for certain elements from pre-defined list
3331   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3332   {
3333     int e = move_stepsize_list[i].element;
3334
3335     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3336
3337     // set move stepsize value for certain elements for older engine versions
3338     if (use_old_move_stepsize_for_magic_wall)
3339     {
3340       if (e == EL_MAGIC_WALL_FILLING ||
3341           e == EL_MAGIC_WALL_EMPTYING ||
3342           e == EL_BD_MAGIC_WALL_FILLING ||
3343           e == EL_BD_MAGIC_WALL_EMPTYING)
3344         element_info[e].move_stepsize *= 2;
3345     }
3346   }
3347
3348   // ---------- initialize collect score --------------------------------------
3349
3350   // initialize collect score values for custom elements from initial value
3351   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3352     if (IS_CUSTOM_ELEMENT(i))
3353       element_info[i].collect_score = element_info[i].collect_score_initial;
3354
3355   // ---------- initialize collect count --------------------------------------
3356
3357   // initialize collect count values for non-custom elements
3358   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3359     if (!IS_CUSTOM_ELEMENT(i))
3360       element_info[i].collect_count_initial = 0;
3361
3362   // add collect count values for all elements from pre-defined list
3363   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3364     element_info[collect_count_list[i].element].collect_count_initial =
3365       collect_count_list[i].count;
3366
3367   // ---------- initialize access direction -----------------------------------
3368
3369   // initialize access direction values to default (access from every side)
3370   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3371     if (!IS_CUSTOM_ELEMENT(i))
3372       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3373
3374   // set access direction value for certain elements from pre-defined list
3375   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3376     element_info[access_direction_list[i].element].access_direction =
3377       access_direction_list[i].direction;
3378
3379   // ---------- initialize explosion content ----------------------------------
3380   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3381   {
3382     if (IS_CUSTOM_ELEMENT(i))
3383       continue;
3384
3385     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3386     {
3387       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3388
3389       element_info[i].content.e[x][y] =
3390         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3391          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3392          i == EL_PLAYER_3 ? EL_EMERALD :
3393          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3394          i == EL_MOLE ? EL_EMERALD_RED :
3395          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3396          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3397          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3398          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3399          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3400          i == EL_WALL_EMERALD ? EL_EMERALD :
3401          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3402          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3403          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3404          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3405          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3406          i == EL_WALL_PEARL ? EL_PEARL :
3407          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3408          EL_EMPTY);
3409     }
3410   }
3411
3412   // ---------- initialize recursion detection --------------------------------
3413   recursion_loop_depth = 0;
3414   recursion_loop_detected = FALSE;
3415   recursion_loop_element = EL_UNDEFINED;
3416
3417   // ---------- initialize graphics engine ------------------------------------
3418   game.scroll_delay_value =
3419     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3420      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3421      !setup.forced_scroll_delay           ? 0 :
3422      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3423   game.scroll_delay_value =
3424     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3425
3426   // ---------- initialize game engine snapshots ------------------------------
3427   for (i = 0; i < MAX_PLAYERS; i++)
3428     game.snapshot.last_action[i] = 0;
3429   game.snapshot.changed_action = FALSE;
3430   game.snapshot.collected_item = FALSE;
3431   game.snapshot.mode =
3432     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3433      SNAPSHOT_MODE_EVERY_STEP :
3434      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3435      SNAPSHOT_MODE_EVERY_MOVE :
3436      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3437      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3438   game.snapshot.save_snapshot = FALSE;
3439
3440   // ---------- initialize level time for Supaplex engine ---------------------
3441   // Supaplex levels with time limit currently unsupported -- should be added
3442   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3443     level.time = 0;
3444
3445   // ---------- initialize flags for handling game actions --------------------
3446
3447   // set flags for game actions to default values
3448   game.use_key_actions = TRUE;
3449   game.use_mouse_actions = FALSE;
3450
3451   // when using Mirror Magic game engine, handle mouse events only
3452   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3453   {
3454     game.use_key_actions = FALSE;
3455     game.use_mouse_actions = TRUE;
3456   }
3457
3458   // check for custom elements with mouse click events
3459   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3460   {
3461     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3462     {
3463       int element = EL_CUSTOM_START + i;
3464
3465       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3466           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3467           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3468           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3469         game.use_mouse_actions = TRUE;
3470     }
3471   }
3472 }
3473
3474 static int get_num_special_action(int element, int action_first,
3475                                   int action_last)
3476 {
3477   int num_special_action = 0;
3478   int i, j;
3479
3480   for (i = action_first; i <= action_last; i++)
3481   {
3482     boolean found = FALSE;
3483
3484     for (j = 0; j < NUM_DIRECTIONS; j++)
3485       if (el_act_dir2img(element, i, j) !=
3486           el_act_dir2img(element, ACTION_DEFAULT, j))
3487         found = TRUE;
3488
3489     if (found)
3490       num_special_action++;
3491     else
3492       break;
3493   }
3494
3495   return num_special_action;
3496 }
3497
3498
3499 // ============================================================================
3500 // InitGame()
3501 // ----------------------------------------------------------------------------
3502 // initialize and start new game
3503 // ============================================================================
3504
3505 #if DEBUG_INIT_PLAYER
3506 static void DebugPrintPlayerStatus(char *message)
3507 {
3508   int i;
3509
3510   if (!options.debug)
3511     return;
3512
3513   Debug("game:init:player", "%s:", message);
3514
3515   for (i = 0; i < MAX_PLAYERS; i++)
3516   {
3517     struct PlayerInfo *player = &stored_player[i];
3518
3519     Debug("game:init:player",
3520           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3521           i + 1,
3522           player->present,
3523           player->connected,
3524           player->connected_locally,
3525           player->connected_network,
3526           player->active,
3527           (local_player == player ? " (local player)" : ""));
3528   }
3529 }
3530 #endif
3531
3532 void InitGame(void)
3533 {
3534   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3535   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3536   int fade_mask = REDRAW_FIELD;
3537
3538   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3539   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3540   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3541   int initial_move_dir = MV_DOWN;
3542   int i, j, x, y;
3543
3544   // required here to update video display before fading (FIX THIS)
3545   DrawMaskedBorder(REDRAW_DOOR_2);
3546
3547   if (!game.restart_level)
3548     CloseDoor(DOOR_CLOSE_1);
3549
3550   SetGameStatus(GAME_MODE_PLAYING);
3551
3552   if (level_editor_test_game)
3553     FadeSkipNextFadeOut();
3554   else
3555     FadeSetEnterScreen();
3556
3557   if (CheckFadeAll())
3558     fade_mask = REDRAW_ALL;
3559
3560   FadeLevelSoundsAndMusic();
3561
3562   ExpireSoundLoops(TRUE);
3563
3564   FadeOut(fade_mask);
3565
3566   if (level_editor_test_game)
3567     FadeSkipNextFadeIn();
3568
3569   // needed if different viewport properties defined for playing
3570   ChangeViewportPropertiesIfNeeded();
3571
3572   ClearField();
3573
3574   DrawCompleteVideoDisplay();
3575
3576   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3577
3578   InitGameEngine();
3579   InitGameControlValues();
3580
3581   if (tape.recording)
3582   {
3583     // initialize tape actions from game when recording tape
3584     tape.use_key_actions   = game.use_key_actions;
3585     tape.use_mouse_actions = game.use_mouse_actions;
3586
3587     // initialize visible playfield size when recording tape (for team mode)
3588     tape.scr_fieldx = SCR_FIELDX;
3589     tape.scr_fieldy = SCR_FIELDY;
3590   }
3591
3592   // don't play tapes over network
3593   network_playing = (network.enabled && !tape.playing);
3594
3595   for (i = 0; i < MAX_PLAYERS; i++)
3596   {
3597     struct PlayerInfo *player = &stored_player[i];
3598
3599     player->index_nr = i;
3600     player->index_bit = (1 << i);
3601     player->element_nr = EL_PLAYER_1 + i;
3602
3603     player->present = FALSE;
3604     player->active = FALSE;
3605     player->mapped = FALSE;
3606
3607     player->killed = FALSE;
3608     player->reanimated = FALSE;
3609     player->buried = FALSE;
3610
3611     player->action = 0;
3612     player->effective_action = 0;
3613     player->programmed_action = 0;
3614     player->snap_action = 0;
3615
3616     player->mouse_action.lx = 0;
3617     player->mouse_action.ly = 0;
3618     player->mouse_action.button = 0;
3619     player->mouse_action.button_hint = 0;
3620
3621     player->effective_mouse_action.lx = 0;
3622     player->effective_mouse_action.ly = 0;
3623     player->effective_mouse_action.button = 0;
3624     player->effective_mouse_action.button_hint = 0;
3625
3626     for (j = 0; j < MAX_NUM_KEYS; j++)
3627       player->key[j] = FALSE;
3628
3629     player->num_white_keys = 0;
3630
3631     player->dynabomb_count = 0;
3632     player->dynabomb_size = 1;
3633     player->dynabombs_left = 0;
3634     player->dynabomb_xl = FALSE;
3635
3636     player->MovDir = initial_move_dir;
3637     player->MovPos = 0;
3638     player->GfxPos = 0;
3639     player->GfxDir = initial_move_dir;
3640     player->GfxAction = ACTION_DEFAULT;
3641     player->Frame = 0;
3642     player->StepFrame = 0;
3643
3644     player->initial_element = player->element_nr;
3645     player->artwork_element =
3646       (level.use_artwork_element[i] ? level.artwork_element[i] :
3647        player->element_nr);
3648     player->use_murphy = FALSE;
3649
3650     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3651     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3652
3653     player->gravity = level.initial_player_gravity[i];
3654
3655     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3656
3657     player->actual_frame_counter = 0;
3658
3659     player->step_counter = 0;
3660
3661     player->last_move_dir = initial_move_dir;
3662
3663     player->is_active = FALSE;
3664
3665     player->is_waiting = FALSE;
3666     player->is_moving = FALSE;
3667     player->is_auto_moving = FALSE;
3668     player->is_digging = FALSE;
3669     player->is_snapping = FALSE;
3670     player->is_collecting = FALSE;
3671     player->is_pushing = FALSE;
3672     player->is_switching = FALSE;
3673     player->is_dropping = FALSE;
3674     player->is_dropping_pressed = FALSE;
3675
3676     player->is_bored = FALSE;
3677     player->is_sleeping = FALSE;
3678
3679     player->was_waiting = TRUE;
3680     player->was_moving = FALSE;
3681     player->was_snapping = FALSE;
3682     player->was_dropping = FALSE;
3683
3684     player->force_dropping = FALSE;
3685
3686     player->frame_counter_bored = -1;
3687     player->frame_counter_sleeping = -1;
3688
3689     player->anim_delay_counter = 0;
3690     player->post_delay_counter = 0;
3691
3692     player->dir_waiting = initial_move_dir;
3693     player->action_waiting = ACTION_DEFAULT;
3694     player->last_action_waiting = ACTION_DEFAULT;
3695     player->special_action_bored = ACTION_DEFAULT;
3696     player->special_action_sleeping = ACTION_DEFAULT;
3697
3698     player->switch_x = -1;
3699     player->switch_y = -1;
3700
3701     player->drop_x = -1;
3702     player->drop_y = -1;
3703
3704     player->show_envelope = 0;
3705
3706     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3707
3708     player->push_delay       = -1;      // initialized when pushing starts
3709     player->push_delay_value = game.initial_push_delay_value;
3710
3711     player->drop_delay = 0;
3712     player->drop_pressed_delay = 0;
3713
3714     player->last_jx = -1;
3715     player->last_jy = -1;
3716     player->jx = -1;
3717     player->jy = -1;
3718
3719     player->shield_normal_time_left = 0;
3720     player->shield_deadly_time_left = 0;
3721
3722     player->last_removed_element = EL_UNDEFINED;
3723
3724     player->inventory_infinite_element = EL_UNDEFINED;
3725     player->inventory_size = 0;
3726
3727     if (level.use_initial_inventory[i])
3728     {
3729       for (j = 0; j < level.initial_inventory_size[i]; j++)
3730       {
3731         int element = level.initial_inventory_content[i][j];
3732         int collect_count = element_info[element].collect_count_initial;
3733         int k;
3734
3735         if (!IS_CUSTOM_ELEMENT(element))
3736           collect_count = 1;
3737
3738         if (collect_count == 0)
3739           player->inventory_infinite_element = element;
3740         else
3741           for (k = 0; k < collect_count; k++)
3742             if (player->inventory_size < MAX_INVENTORY_SIZE)
3743               player->inventory_element[player->inventory_size++] = element;
3744       }
3745     }
3746
3747     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3748     SnapField(player, 0, 0);
3749
3750     map_player_action[i] = i;
3751   }
3752
3753   network_player_action_received = FALSE;
3754
3755   // initial null action
3756   if (network_playing)
3757     SendToServer_MovePlayer(MV_NONE);
3758
3759   FrameCounter = 0;
3760   TimeFrames = 0;
3761   TimePlayed = 0;
3762   TimeLeft = level.time;
3763   TapeTime = 0;
3764
3765   ScreenMovDir = MV_NONE;
3766   ScreenMovPos = 0;
3767   ScreenGfxPos = 0;
3768
3769   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3770
3771   game.robot_wheel_x = -1;
3772   game.robot_wheel_y = -1;
3773
3774   game.exit_x = -1;
3775   game.exit_y = -1;
3776
3777   game.all_players_gone = FALSE;
3778
3779   game.LevelSolved = FALSE;
3780   game.GameOver = FALSE;
3781
3782   game.GamePlayed = !tape.playing;
3783
3784   game.LevelSolved_GameWon = FALSE;
3785   game.LevelSolved_GameEnd = FALSE;
3786   game.LevelSolved_SaveTape = FALSE;
3787   game.LevelSolved_SaveScore = FALSE;
3788
3789   game.LevelSolved_CountingTime = 0;
3790   game.LevelSolved_CountingScore = 0;
3791   game.LevelSolved_CountingHealth = 0;
3792
3793   game.panel.active = TRUE;
3794
3795   game.no_time_limit = (level.time == 0);
3796
3797   game.yamyam_content_nr = 0;
3798   game.robot_wheel_active = FALSE;
3799   game.magic_wall_active = FALSE;
3800   game.magic_wall_time_left = 0;
3801   game.light_time_left = 0;
3802   game.timegate_time_left = 0;
3803   game.switchgate_pos = 0;
3804   game.wind_direction = level.wind_direction_initial;
3805
3806   game.score = 0;
3807   game.score_final = 0;
3808
3809   game.health = MAX_HEALTH;
3810   game.health_final = MAX_HEALTH;
3811
3812   game.gems_still_needed = level.gems_needed;
3813   game.sokoban_fields_still_needed = 0;
3814   game.sokoban_objects_still_needed = 0;
3815   game.lights_still_needed = 0;
3816   game.players_still_needed = 0;
3817   game.friends_still_needed = 0;
3818
3819   game.lenses_time_left = 0;
3820   game.magnify_time_left = 0;
3821
3822   game.ball_active = level.ball_active_initial;
3823   game.ball_content_nr = 0;
3824
3825   game.explosions_delayed = TRUE;
3826
3827   game.envelope_active = FALSE;
3828
3829   for (i = 0; i < NUM_BELTS; i++)
3830   {
3831     game.belt_dir[i] = MV_NONE;
3832     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3833   }
3834
3835   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3836     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3837
3838 #if DEBUG_INIT_PLAYER
3839   DebugPrintPlayerStatus("Player status at level initialization");
3840 #endif
3841
3842   SCAN_PLAYFIELD(x, y)
3843   {
3844     Tile[x][y] = Last[x][y] = level.field[x][y];
3845     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3846     ChangeDelay[x][y] = 0;
3847     ChangePage[x][y] = -1;
3848     CustomValue[x][y] = 0;              // initialized in InitField()
3849     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3850     AmoebaNr[x][y] = 0;
3851     WasJustMoving[x][y] = 0;
3852     WasJustFalling[x][y] = 0;
3853     CheckCollision[x][y] = 0;
3854     CheckImpact[x][y] = 0;
3855     Stop[x][y] = FALSE;
3856     Pushed[x][y] = FALSE;
3857
3858     ChangeCount[x][y] = 0;
3859     ChangeEvent[x][y] = -1;
3860
3861     ExplodePhase[x][y] = 0;
3862     ExplodeDelay[x][y] = 0;
3863     ExplodeField[x][y] = EX_TYPE_NONE;
3864
3865     RunnerVisit[x][y] = 0;
3866     PlayerVisit[x][y] = 0;
3867
3868     GfxFrame[x][y] = 0;
3869     GfxRandom[x][y] = INIT_GFX_RANDOM();
3870     GfxElement[x][y] = EL_UNDEFINED;
3871     GfxAction[x][y] = ACTION_DEFAULT;
3872     GfxDir[x][y] = MV_NONE;
3873     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3874   }
3875
3876   SCAN_PLAYFIELD(x, y)
3877   {
3878     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3879       emulate_bd = FALSE;
3880     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3881       emulate_sb = FALSE;
3882     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3883       emulate_sp = FALSE;
3884
3885     InitField(x, y, TRUE);
3886
3887     ResetGfxAnimation(x, y);
3888   }
3889
3890   InitBeltMovement();
3891
3892   for (i = 0; i < MAX_PLAYERS; i++)
3893   {
3894     struct PlayerInfo *player = &stored_player[i];
3895
3896     // set number of special actions for bored and sleeping animation
3897     player->num_special_action_bored =
3898       get_num_special_action(player->artwork_element,
3899                              ACTION_BORING_1, ACTION_BORING_LAST);
3900     player->num_special_action_sleeping =
3901       get_num_special_action(player->artwork_element,
3902                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3903   }
3904
3905   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3906                     emulate_sb ? EMU_SOKOBAN :
3907                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3908
3909   // initialize type of slippery elements
3910   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3911   {
3912     if (!IS_CUSTOM_ELEMENT(i))
3913     {
3914       // default: elements slip down either to the left or right randomly
3915       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3916
3917       // SP style elements prefer to slip down on the left side
3918       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3919         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3920
3921       // BD style elements prefer to slip down on the left side
3922       if (game.emulation == EMU_BOULDERDASH)
3923         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3924     }
3925   }
3926
3927   // initialize explosion and ignition delay
3928   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3929   {
3930     if (!IS_CUSTOM_ELEMENT(i))
3931     {
3932       int num_phase = 8;
3933       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3934                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3935                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3936       int last_phase = (num_phase + 1) * delay;
3937       int half_phase = (num_phase / 2) * delay;
3938
3939       element_info[i].explosion_delay = last_phase - 1;
3940       element_info[i].ignition_delay = half_phase;
3941
3942       if (i == EL_BLACK_ORB)
3943         element_info[i].ignition_delay = 1;
3944     }
3945   }
3946
3947   // correct non-moving belts to start moving left
3948   for (i = 0; i < NUM_BELTS; i++)
3949     if (game.belt_dir[i] == MV_NONE)
3950       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3951
3952 #if USE_NEW_PLAYER_ASSIGNMENTS
3953   // use preferred player also in local single-player mode
3954   if (!network.enabled && !game.team_mode)
3955   {
3956     int new_index_nr = setup.network_player_nr;
3957
3958     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3959     {
3960       for (i = 0; i < MAX_PLAYERS; i++)
3961         stored_player[i].connected_locally = FALSE;
3962
3963       stored_player[new_index_nr].connected_locally = TRUE;
3964     }
3965   }
3966
3967   for (i = 0; i < MAX_PLAYERS; i++)
3968   {
3969     stored_player[i].connected = FALSE;
3970
3971     // in network game mode, the local player might not be the first player
3972     if (stored_player[i].connected_locally)
3973       local_player = &stored_player[i];
3974   }
3975
3976   if (!network.enabled)
3977     local_player->connected = TRUE;
3978
3979   if (tape.playing)
3980   {
3981     for (i = 0; i < MAX_PLAYERS; i++)
3982       stored_player[i].connected = tape.player_participates[i];
3983   }
3984   else if (network.enabled)
3985   {
3986     // add team mode players connected over the network (needed for correct
3987     // assignment of player figures from level to locally playing players)
3988
3989     for (i = 0; i < MAX_PLAYERS; i++)
3990       if (stored_player[i].connected_network)
3991         stored_player[i].connected = TRUE;
3992   }
3993   else if (game.team_mode)
3994   {
3995     // try to guess locally connected team mode players (needed for correct
3996     // assignment of player figures from level to locally playing players)
3997
3998     for (i = 0; i < MAX_PLAYERS; i++)
3999       if (setup.input[i].use_joystick ||
4000           setup.input[i].key.left != KSYM_UNDEFINED)
4001         stored_player[i].connected = TRUE;
4002   }
4003
4004 #if DEBUG_INIT_PLAYER
4005   DebugPrintPlayerStatus("Player status after level initialization");
4006 #endif
4007
4008 #if DEBUG_INIT_PLAYER
4009   Debug("game:init:player", "Reassigning players ...");
4010 #endif
4011
4012   // check if any connected player was not found in playfield
4013   for (i = 0; i < MAX_PLAYERS; i++)
4014   {
4015     struct PlayerInfo *player = &stored_player[i];
4016
4017     if (player->connected && !player->present)
4018     {
4019       struct PlayerInfo *field_player = NULL;
4020
4021 #if DEBUG_INIT_PLAYER
4022       Debug("game:init:player",
4023             "- looking for field player for player %d ...", i + 1);
4024 #endif
4025
4026       // assign first free player found that is present in the playfield
4027
4028       // first try: look for unmapped playfield player that is not connected
4029       for (j = 0; j < MAX_PLAYERS; j++)
4030         if (field_player == NULL &&
4031             stored_player[j].present &&
4032             !stored_player[j].mapped &&
4033             !stored_player[j].connected)
4034           field_player = &stored_player[j];
4035
4036       // second try: look for *any* unmapped playfield player
4037       for (j = 0; j < MAX_PLAYERS; j++)
4038         if (field_player == NULL &&
4039             stored_player[j].present &&
4040             !stored_player[j].mapped)
4041           field_player = &stored_player[j];
4042
4043       if (field_player != NULL)
4044       {
4045         int jx = field_player->jx, jy = field_player->jy;
4046
4047 #if DEBUG_INIT_PLAYER
4048         Debug("game:init:player", "- found player %d",
4049               field_player->index_nr + 1);
4050 #endif
4051
4052         player->present = FALSE;
4053         player->active = FALSE;
4054
4055         field_player->present = TRUE;
4056         field_player->active = TRUE;
4057
4058         /*
4059         player->initial_element = field_player->initial_element;
4060         player->artwork_element = field_player->artwork_element;
4061
4062         player->block_last_field       = field_player->block_last_field;
4063         player->block_delay_adjustment = field_player->block_delay_adjustment;
4064         */
4065
4066         StorePlayer[jx][jy] = field_player->element_nr;
4067
4068         field_player->jx = field_player->last_jx = jx;
4069         field_player->jy = field_player->last_jy = jy;
4070
4071         if (local_player == player)
4072           local_player = field_player;
4073
4074         map_player_action[field_player->index_nr] = i;
4075
4076         field_player->mapped = TRUE;
4077
4078 #if DEBUG_INIT_PLAYER
4079         Debug("game:init:player", "- map_player_action[%d] == %d",
4080               field_player->index_nr + 1, i + 1);
4081 #endif
4082       }
4083     }
4084
4085     if (player->connected && player->present)
4086       player->mapped = TRUE;
4087   }
4088
4089 #if DEBUG_INIT_PLAYER
4090   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4091 #endif
4092
4093 #else
4094
4095   // check if any connected player was not found in playfield
4096   for (i = 0; i < MAX_PLAYERS; i++)
4097   {
4098     struct PlayerInfo *player = &stored_player[i];
4099
4100     if (player->connected && !player->present)
4101     {
4102       for (j = 0; j < MAX_PLAYERS; j++)
4103       {
4104         struct PlayerInfo *field_player = &stored_player[j];
4105         int jx = field_player->jx, jy = field_player->jy;
4106
4107         // assign first free player found that is present in the playfield
4108         if (field_player->present && !field_player->connected)
4109         {
4110           player->present = TRUE;
4111           player->active = TRUE;
4112
4113           field_player->present = FALSE;
4114           field_player->active = FALSE;
4115
4116           player->initial_element = field_player->initial_element;
4117           player->artwork_element = field_player->artwork_element;
4118
4119           player->block_last_field       = field_player->block_last_field;
4120           player->block_delay_adjustment = field_player->block_delay_adjustment;
4121
4122           StorePlayer[jx][jy] = player->element_nr;
4123
4124           player->jx = player->last_jx = jx;
4125           player->jy = player->last_jy = jy;
4126
4127           break;
4128         }
4129       }
4130     }
4131   }
4132 #endif
4133
4134 #if 0
4135   Debug("game:init:player", "local_player->present == %d",
4136         local_player->present);
4137 #endif
4138
4139   // set focus to local player for network games, else to all players
4140   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4141   game.centered_player_nr_next = game.centered_player_nr;
4142   game.set_centered_player = FALSE;
4143   game.set_centered_player_wrap = FALSE;
4144
4145   if (network_playing && tape.recording)
4146   {
4147     // store client dependent player focus when recording network games
4148     tape.centered_player_nr_next = game.centered_player_nr_next;
4149     tape.set_centered_player = TRUE;
4150   }
4151
4152   if (tape.playing)
4153   {
4154     // when playing a tape, eliminate all players who do not participate
4155
4156 #if USE_NEW_PLAYER_ASSIGNMENTS
4157
4158     if (!game.team_mode)
4159     {
4160       for (i = 0; i < MAX_PLAYERS; i++)
4161       {
4162         if (stored_player[i].active &&
4163             !tape.player_participates[map_player_action[i]])
4164         {
4165           struct PlayerInfo *player = &stored_player[i];
4166           int jx = player->jx, jy = player->jy;
4167
4168 #if DEBUG_INIT_PLAYER
4169           Debug("game:init:player", "Removing player %d at (%d, %d)",
4170                 i + 1, jx, jy);
4171 #endif
4172
4173           player->active = FALSE;
4174           StorePlayer[jx][jy] = 0;
4175           Tile[jx][jy] = EL_EMPTY;
4176         }
4177       }
4178     }
4179
4180 #else
4181
4182     for (i = 0; i < MAX_PLAYERS; i++)
4183     {
4184       if (stored_player[i].active &&
4185           !tape.player_participates[i])
4186       {
4187         struct PlayerInfo *player = &stored_player[i];
4188         int jx = player->jx, jy = player->jy;
4189
4190         player->active = FALSE;
4191         StorePlayer[jx][jy] = 0;
4192         Tile[jx][jy] = EL_EMPTY;
4193       }
4194     }
4195 #endif
4196   }
4197   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4198   {
4199     // when in single player mode, eliminate all but the local player
4200
4201     for (i = 0; i < MAX_PLAYERS; i++)
4202     {
4203       struct PlayerInfo *player = &stored_player[i];
4204
4205       if (player->active && player != local_player)
4206       {
4207         int jx = player->jx, jy = player->jy;
4208
4209         player->active = FALSE;
4210         player->present = FALSE;
4211
4212         StorePlayer[jx][jy] = 0;
4213         Tile[jx][jy] = EL_EMPTY;
4214       }
4215     }
4216   }
4217
4218   for (i = 0; i < MAX_PLAYERS; i++)
4219     if (stored_player[i].active)
4220       game.players_still_needed++;
4221
4222   if (level.solved_by_one_player)
4223     game.players_still_needed = 1;
4224
4225   // when recording the game, store which players take part in the game
4226   if (tape.recording)
4227   {
4228 #if USE_NEW_PLAYER_ASSIGNMENTS
4229     for (i = 0; i < MAX_PLAYERS; i++)
4230       if (stored_player[i].connected)
4231         tape.player_participates[i] = TRUE;
4232 #else
4233     for (i = 0; i < MAX_PLAYERS; i++)
4234       if (stored_player[i].active)
4235         tape.player_participates[i] = TRUE;
4236 #endif
4237   }
4238
4239 #if DEBUG_INIT_PLAYER
4240   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4241 #endif
4242
4243   if (BorderElement == EL_EMPTY)
4244   {
4245     SBX_Left = 0;
4246     SBX_Right = lev_fieldx - SCR_FIELDX;
4247     SBY_Upper = 0;
4248     SBY_Lower = lev_fieldy - SCR_FIELDY;
4249   }
4250   else
4251   {
4252     SBX_Left = -1;
4253     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4254     SBY_Upper = -1;
4255     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4256   }
4257
4258   if (full_lev_fieldx <= SCR_FIELDX)
4259     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4260   if (full_lev_fieldy <= SCR_FIELDY)
4261     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4262
4263   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4264     SBX_Left--;
4265   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4266     SBY_Upper--;
4267
4268   // if local player not found, look for custom element that might create
4269   // the player (make some assumptions about the right custom element)
4270   if (!local_player->present)
4271   {
4272     int start_x = 0, start_y = 0;
4273     int found_rating = 0;
4274     int found_element = EL_UNDEFINED;
4275     int player_nr = local_player->index_nr;
4276
4277     SCAN_PLAYFIELD(x, y)
4278     {
4279       int element = Tile[x][y];
4280       int content;
4281       int xx, yy;
4282       boolean is_player;
4283
4284       if (level.use_start_element[player_nr] &&
4285           level.start_element[player_nr] == element &&
4286           found_rating < 4)
4287       {
4288         start_x = x;
4289         start_y = y;
4290
4291         found_rating = 4;
4292         found_element = element;
4293       }
4294
4295       if (!IS_CUSTOM_ELEMENT(element))
4296         continue;
4297
4298       if (CAN_CHANGE(element))
4299       {
4300         for (i = 0; i < element_info[element].num_change_pages; i++)
4301         {
4302           // check for player created from custom element as single target
4303           content = element_info[element].change_page[i].target_element;
4304           is_player = ELEM_IS_PLAYER(content);
4305
4306           if (is_player && (found_rating < 3 ||
4307                             (found_rating == 3 && element < found_element)))
4308           {
4309             start_x = x;
4310             start_y = y;
4311
4312             found_rating = 3;
4313             found_element = element;
4314           }
4315         }
4316       }
4317
4318       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4319       {
4320         // check for player created from custom element as explosion content
4321         content = element_info[element].content.e[xx][yy];
4322         is_player = ELEM_IS_PLAYER(content);
4323
4324         if (is_player && (found_rating < 2 ||
4325                           (found_rating == 2 && element < found_element)))
4326         {
4327           start_x = x + xx - 1;
4328           start_y = y + yy - 1;
4329
4330           found_rating = 2;
4331           found_element = element;
4332         }
4333
4334         if (!CAN_CHANGE(element))
4335           continue;
4336
4337         for (i = 0; i < element_info[element].num_change_pages; i++)
4338         {
4339           // check for player created from custom element as extended target
4340           content =
4341             element_info[element].change_page[i].target_content.e[xx][yy];
4342
4343           is_player = ELEM_IS_PLAYER(content);
4344
4345           if (is_player && (found_rating < 1 ||
4346                             (found_rating == 1 && element < found_element)))
4347           {
4348             start_x = x + xx - 1;
4349             start_y = y + yy - 1;
4350
4351             found_rating = 1;
4352             found_element = element;
4353           }
4354         }
4355       }
4356     }
4357
4358     scroll_x = SCROLL_POSITION_X(start_x);
4359     scroll_y = SCROLL_POSITION_Y(start_y);
4360   }
4361   else
4362   {
4363     scroll_x = SCROLL_POSITION_X(local_player->jx);
4364     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4365   }
4366
4367   // !!! FIX THIS (START) !!!
4368   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4369   {
4370     InitGameEngine_EM();
4371   }
4372   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4373   {
4374     InitGameEngine_SP();
4375   }
4376   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4377   {
4378     InitGameEngine_MM();
4379   }
4380   else
4381   {
4382     DrawLevel(REDRAW_FIELD);
4383     DrawAllPlayers();
4384
4385     // after drawing the level, correct some elements
4386     if (game.timegate_time_left == 0)
4387       CloseAllOpenTimegates();
4388   }
4389
4390   // blit playfield from scroll buffer to normal back buffer for fading in
4391   BlitScreenToBitmap(backbuffer);
4392   // !!! FIX THIS (END) !!!
4393
4394   DrawMaskedBorder(fade_mask);
4395
4396   FadeIn(fade_mask);
4397
4398 #if 1
4399   // full screen redraw is required at this point in the following cases:
4400   // - special editor door undrawn when game was started from level editor
4401   // - drawing area (playfield) was changed and has to be removed completely
4402   redraw_mask = REDRAW_ALL;
4403   BackToFront();
4404 #endif
4405
4406   if (!game.restart_level)
4407   {
4408     // copy default game door content to main double buffer
4409
4410     // !!! CHECK AGAIN !!!
4411     SetPanelBackground();
4412     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4413     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4414   }
4415
4416   SetPanelBackground();
4417   SetDrawBackgroundMask(REDRAW_DOOR_1);
4418
4419   UpdateAndDisplayGameControlValues();
4420
4421   if (!game.restart_level)
4422   {
4423     UnmapGameButtons();
4424     UnmapTapeButtons();
4425
4426     FreeGameButtons();
4427     CreateGameButtons();
4428
4429     MapGameButtons();
4430     MapTapeButtons();
4431
4432     // copy actual game door content to door double buffer for OpenDoor()
4433     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4434
4435     OpenDoor(DOOR_OPEN_ALL);
4436
4437     KeyboardAutoRepeatOffUnlessAutoplay();
4438
4439 #if DEBUG_INIT_PLAYER
4440     DebugPrintPlayerStatus("Player status (final)");
4441 #endif
4442   }
4443
4444   UnmapAllGadgets();
4445
4446   MapGameButtons();
4447   MapTapeButtons();
4448
4449   if (!game.restart_level && !tape.playing)
4450   {
4451     LevelStats_incPlayed(level_nr);
4452
4453     SaveLevelSetup_SeriesInfo();
4454   }
4455
4456   game.restart_level = FALSE;
4457   game.restart_game_message = NULL;
4458
4459   game.request_active = FALSE;
4460   game.request_active_or_moving = FALSE;
4461
4462   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4463     InitGameActions_MM();
4464
4465   SaveEngineSnapshotToListInitial();
4466
4467   if (!game.restart_level)
4468   {
4469     PlaySound(SND_GAME_STARTING);
4470
4471     if (setup.sound_music)
4472       PlayLevelMusic();
4473   }
4474 }
4475
4476 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4477                         int actual_player_x, int actual_player_y)
4478 {
4479   // this is used for non-R'n'D game engines to update certain engine values
4480
4481   // needed to determine if sounds are played within the visible screen area
4482   scroll_x = actual_scroll_x;
4483   scroll_y = actual_scroll_y;
4484
4485   // needed to get player position for "follow finger" playing input method
4486   local_player->jx = actual_player_x;
4487   local_player->jy = actual_player_y;
4488 }
4489
4490 void InitMovDir(int x, int y)
4491 {
4492   int i, element = Tile[x][y];
4493   static int xy[4][2] =
4494   {
4495     {  0, +1 },
4496     { +1,  0 },
4497     {  0, -1 },
4498     { -1,  0 }
4499   };
4500   static int direction[3][4] =
4501   {
4502     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4503     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4504     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4505   };
4506
4507   switch (element)
4508   {
4509     case EL_BUG_RIGHT:
4510     case EL_BUG_UP:
4511     case EL_BUG_LEFT:
4512     case EL_BUG_DOWN:
4513       Tile[x][y] = EL_BUG;
4514       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4515       break;
4516
4517     case EL_SPACESHIP_RIGHT:
4518     case EL_SPACESHIP_UP:
4519     case EL_SPACESHIP_LEFT:
4520     case EL_SPACESHIP_DOWN:
4521       Tile[x][y] = EL_SPACESHIP;
4522       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4523       break;
4524
4525     case EL_BD_BUTTERFLY_RIGHT:
4526     case EL_BD_BUTTERFLY_UP:
4527     case EL_BD_BUTTERFLY_LEFT:
4528     case EL_BD_BUTTERFLY_DOWN:
4529       Tile[x][y] = EL_BD_BUTTERFLY;
4530       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4531       break;
4532
4533     case EL_BD_FIREFLY_RIGHT:
4534     case EL_BD_FIREFLY_UP:
4535     case EL_BD_FIREFLY_LEFT:
4536     case EL_BD_FIREFLY_DOWN:
4537       Tile[x][y] = EL_BD_FIREFLY;
4538       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4539       break;
4540
4541     case EL_PACMAN_RIGHT:
4542     case EL_PACMAN_UP:
4543     case EL_PACMAN_LEFT:
4544     case EL_PACMAN_DOWN:
4545       Tile[x][y] = EL_PACMAN;
4546       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4547       break;
4548
4549     case EL_YAMYAM_LEFT:
4550     case EL_YAMYAM_RIGHT:
4551     case EL_YAMYAM_UP:
4552     case EL_YAMYAM_DOWN:
4553       Tile[x][y] = EL_YAMYAM;
4554       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4555       break;
4556
4557     case EL_SP_SNIKSNAK:
4558       MovDir[x][y] = MV_UP;
4559       break;
4560
4561     case EL_SP_ELECTRON:
4562       MovDir[x][y] = MV_LEFT;
4563       break;
4564
4565     case EL_MOLE_LEFT:
4566     case EL_MOLE_RIGHT:
4567     case EL_MOLE_UP:
4568     case EL_MOLE_DOWN:
4569       Tile[x][y] = EL_MOLE;
4570       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4571       break;
4572
4573     case EL_SPRING_LEFT:
4574     case EL_SPRING_RIGHT:
4575       Tile[x][y] = EL_SPRING;
4576       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4577       break;
4578
4579     default:
4580       if (IS_CUSTOM_ELEMENT(element))
4581       {
4582         struct ElementInfo *ei = &element_info[element];
4583         int move_direction_initial = ei->move_direction_initial;
4584         int move_pattern = ei->move_pattern;
4585
4586         if (move_direction_initial == MV_START_PREVIOUS)
4587         {
4588           if (MovDir[x][y] != MV_NONE)
4589             return;
4590
4591           move_direction_initial = MV_START_AUTOMATIC;
4592         }
4593
4594         if (move_direction_initial == MV_START_RANDOM)
4595           MovDir[x][y] = 1 << RND(4);
4596         else if (move_direction_initial & MV_ANY_DIRECTION)
4597           MovDir[x][y] = move_direction_initial;
4598         else if (move_pattern == MV_ALL_DIRECTIONS ||
4599                  move_pattern == MV_TURNING_LEFT ||
4600                  move_pattern == MV_TURNING_RIGHT ||
4601                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4602                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4603                  move_pattern == MV_TURNING_RANDOM)
4604           MovDir[x][y] = 1 << RND(4);
4605         else if (move_pattern == MV_HORIZONTAL)
4606           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4607         else if (move_pattern == MV_VERTICAL)
4608           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4609         else if (move_pattern & MV_ANY_DIRECTION)
4610           MovDir[x][y] = element_info[element].move_pattern;
4611         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4612                  move_pattern == MV_ALONG_RIGHT_SIDE)
4613         {
4614           // use random direction as default start direction
4615           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4616             MovDir[x][y] = 1 << RND(4);
4617
4618           for (i = 0; i < NUM_DIRECTIONS; i++)
4619           {
4620             int x1 = x + xy[i][0];
4621             int y1 = y + xy[i][1];
4622
4623             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4624             {
4625               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4626                 MovDir[x][y] = direction[0][i];
4627               else
4628                 MovDir[x][y] = direction[1][i];
4629
4630               break;
4631             }
4632           }
4633         }                
4634       }
4635       else
4636       {
4637         MovDir[x][y] = 1 << RND(4);
4638
4639         if (element != EL_BUG &&
4640             element != EL_SPACESHIP &&
4641             element != EL_BD_BUTTERFLY &&
4642             element != EL_BD_FIREFLY)
4643           break;
4644
4645         for (i = 0; i < NUM_DIRECTIONS; i++)
4646         {
4647           int x1 = x + xy[i][0];
4648           int y1 = y + xy[i][1];
4649
4650           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4651           {
4652             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4653             {
4654               MovDir[x][y] = direction[0][i];
4655               break;
4656             }
4657             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4658                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4659             {
4660               MovDir[x][y] = direction[1][i];
4661               break;
4662             }
4663           }
4664         }
4665       }
4666       break;
4667   }
4668
4669   GfxDir[x][y] = MovDir[x][y];
4670 }
4671
4672 void InitAmoebaNr(int x, int y)
4673 {
4674   int i;
4675   int group_nr = AmoebaNeighbourNr(x, y);
4676
4677   if (group_nr == 0)
4678   {
4679     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4680     {
4681       if (AmoebaCnt[i] == 0)
4682       {
4683         group_nr = i;
4684         break;
4685       }
4686     }
4687   }
4688
4689   AmoebaNr[x][y] = group_nr;
4690   AmoebaCnt[group_nr]++;
4691   AmoebaCnt2[group_nr]++;
4692 }
4693
4694 static void LevelSolved(void)
4695 {
4696   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4697       game.players_still_needed > 0)
4698     return;
4699
4700   game.LevelSolved = TRUE;
4701   game.GameOver = TRUE;
4702
4703   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4704                       game_em.lev->score :
4705                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4706                       game_mm.score :
4707                       game.score);
4708   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4709                        MM_HEALTH(game_mm.laser_overload_value) :
4710                        game.health);
4711
4712   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4713   game.LevelSolved_CountingScore = game.score_final;
4714   game.LevelSolved_CountingHealth = game.health_final;
4715 }
4716
4717 void GameWon(void)
4718 {
4719   static int time_count_steps;
4720   static int time, time_final;
4721   static float score, score_final; // needed for time score < 10 for 10 seconds
4722   static int health, health_final;
4723   static int game_over_delay_1 = 0;
4724   static int game_over_delay_2 = 0;
4725   static int game_over_delay_3 = 0;
4726   int game_over_delay_value_1 = 50;
4727   int game_over_delay_value_2 = 25;
4728   int game_over_delay_value_3 = 50;
4729   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4730   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4731
4732   if (!game.LevelSolved_GameWon)
4733   {
4734     int i;
4735
4736     // do not start end game actions before the player stops moving (to exit)
4737     if (local_player->active && local_player->MovPos)
4738       return;
4739
4740     game.LevelSolved_GameWon = TRUE;
4741     game.LevelSolved_SaveTape = tape.recording;
4742     game.LevelSolved_SaveScore = !tape.playing;
4743
4744     if (!tape.playing)
4745     {
4746       LevelStats_incSolved(level_nr);
4747
4748       SaveLevelSetup_SeriesInfo();
4749     }
4750
4751     if (tape.auto_play)         // tape might already be stopped here
4752       tape.auto_play_level_solved = TRUE;
4753
4754     TapeStop();
4755
4756     game_over_delay_1 = 0;
4757     game_over_delay_2 = 0;
4758     game_over_delay_3 = game_over_delay_value_3;
4759
4760     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4761     score = score_final = game.score_final;
4762     health = health_final = game.health_final;
4763
4764     if (time_score > 0)
4765     {
4766       int time_frames = 0;
4767
4768       if (TimeLeft > 0)
4769       {
4770         time_final = 0;
4771         time_frames = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4772       }
4773       else if (game.no_time_limit && TimePlayed < 999)
4774       {
4775         time_final = 999;
4776         time_frames = (999 - TimePlayed) * FRAMES_PER_SECOND - TimeFrames;
4777       }
4778
4779       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4780
4781       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4782
4783       game_over_delay_1 = game_over_delay_value_1;
4784
4785       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4786       {
4787         health_final = 0;
4788         score_final += health * time_score;
4789
4790         game_over_delay_2 = game_over_delay_value_2;
4791       }
4792
4793       game.score_final = score_final;
4794       game.health_final = health_final;
4795     }
4796
4797     if (level_editor_test_game)
4798     {
4799       time = time_final;
4800       score = score_final;
4801
4802       game.LevelSolved_CountingTime = time;
4803       game.LevelSolved_CountingScore = score;
4804
4805       game_panel_controls[GAME_PANEL_TIME].value = time;
4806       game_panel_controls[GAME_PANEL_SCORE].value = score;
4807
4808       DisplayGameControlValues();
4809     }
4810
4811     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4812     {
4813       // check if last player has left the level
4814       if (game.exit_x >= 0 &&
4815           game.exit_y >= 0)
4816       {
4817         int x = game.exit_x;
4818         int y = game.exit_y;
4819         int element = Tile[x][y];
4820
4821         // close exit door after last player
4822         if ((game.all_players_gone &&
4823              (element == EL_EXIT_OPEN ||
4824               element == EL_SP_EXIT_OPEN ||
4825               element == EL_STEEL_EXIT_OPEN)) ||
4826             element == EL_EM_EXIT_OPEN ||
4827             element == EL_EM_STEEL_EXIT_OPEN)
4828         {
4829
4830           Tile[x][y] =
4831             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4832              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4833              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4834              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4835              EL_EM_STEEL_EXIT_CLOSING);
4836
4837           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4838         }
4839
4840         // player disappears
4841         DrawLevelField(x, y);
4842       }
4843
4844       for (i = 0; i < MAX_PLAYERS; i++)
4845       {
4846         struct PlayerInfo *player = &stored_player[i];
4847
4848         if (player->present)
4849         {
4850           RemovePlayer(player);
4851
4852           // player disappears
4853           DrawLevelField(player->jx, player->jy);
4854         }
4855       }
4856     }
4857
4858     PlaySound(SND_GAME_WINNING);
4859   }
4860
4861   if (game_over_delay_1 > 0)
4862   {
4863     game_over_delay_1--;
4864
4865     return;
4866   }
4867
4868   if (time != time_final)
4869   {
4870     int time_to_go = ABS(time_final - time);
4871     int time_count_dir = (time < time_final ? +1 : -1);
4872
4873     if (time_to_go < time_count_steps)
4874       time_count_steps = 1;
4875
4876     time  += time_count_steps * time_count_dir;
4877     score += time_count_steps * time_score;
4878
4879     // set final score to correct rounding differences after counting score
4880     if (time == time_final)
4881       score = score_final;
4882
4883     game.LevelSolved_CountingTime = time;
4884     game.LevelSolved_CountingScore = score;
4885
4886     game_panel_controls[GAME_PANEL_TIME].value = time;
4887     game_panel_controls[GAME_PANEL_SCORE].value = score;
4888
4889     DisplayGameControlValues();
4890
4891     if (time == time_final)
4892       StopSound(SND_GAME_LEVELTIME_BONUS);
4893     else if (setup.sound_loops)
4894       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4895     else
4896       PlaySound(SND_GAME_LEVELTIME_BONUS);
4897
4898     return;
4899   }
4900
4901   if (game_over_delay_2 > 0)
4902   {
4903     game_over_delay_2--;
4904
4905     return;
4906   }
4907
4908   if (health != health_final)
4909   {
4910     int health_count_dir = (health < health_final ? +1 : -1);
4911
4912     health += health_count_dir;
4913     score  += time_score;
4914
4915     game.LevelSolved_CountingHealth = health;
4916     game.LevelSolved_CountingScore = score;
4917
4918     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4919     game_panel_controls[GAME_PANEL_SCORE].value = score;
4920
4921     DisplayGameControlValues();
4922
4923     if (health == health_final)
4924       StopSound(SND_GAME_LEVELTIME_BONUS);
4925     else if (setup.sound_loops)
4926       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4927     else
4928       PlaySound(SND_GAME_LEVELTIME_BONUS);
4929
4930     return;
4931   }
4932
4933   game.panel.active = FALSE;
4934
4935   if (game_over_delay_3 > 0)
4936   {
4937     game_over_delay_3--;
4938
4939     return;
4940   }
4941
4942   GameEnd();
4943 }
4944
4945 void GameEnd(void)
4946 {
4947   // used instead of "level_nr" (needed for network games)
4948   int last_level_nr = levelset.level_nr;
4949   int hi_pos;
4950
4951   game.LevelSolved_GameEnd = TRUE;
4952
4953   if (game.LevelSolved_SaveTape)
4954   {
4955     // make sure that request dialog to save tape does not open door again
4956     if (!global.use_envelope_request)
4957       CloseDoor(DOOR_CLOSE_1);
4958
4959     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4960   }
4961
4962   // if no tape is to be saved, close both doors simultaneously
4963   CloseDoor(DOOR_CLOSE_ALL);
4964
4965   if (level_editor_test_game)
4966   {
4967     SetGameStatus(GAME_MODE_MAIN);
4968
4969     DrawMainMenu();
4970
4971     return;
4972   }
4973
4974   if (!game.LevelSolved_SaveScore)
4975   {
4976     SetGameStatus(GAME_MODE_MAIN);
4977
4978     DrawMainMenu();
4979
4980     return;
4981   }
4982
4983   if (level_nr == leveldir_current->handicap_level)
4984   {
4985     leveldir_current->handicap_level++;
4986
4987     SaveLevelSetup_SeriesInfo();
4988   }
4989
4990   if (setup.increment_levels &&
4991       level_nr < leveldir_current->last_level &&
4992       !network_playing)
4993   {
4994     level_nr++;         // advance to next level
4995     TapeErase();        // start with empty tape
4996
4997     if (setup.auto_play_next_level)
4998     {
4999       LoadLevel(level_nr);
5000
5001       SaveLevelSetup_SeriesInfo();
5002     }
5003   }
5004
5005   hi_pos = NewHiScore(last_level_nr);
5006
5007   if (hi_pos >= 0 && !setup.skip_scores_after_game)
5008   {
5009     SetGameStatus(GAME_MODE_SCORES);
5010
5011     DrawHallOfFame(last_level_nr, hi_pos);
5012   }
5013   else if (setup.auto_play_next_level && setup.increment_levels &&
5014            last_level_nr < leveldir_current->last_level &&
5015            !network_playing)
5016   {
5017     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5018   }
5019   else
5020   {
5021     SetGameStatus(GAME_MODE_MAIN);
5022
5023     DrawMainMenu();
5024   }
5025 }
5026
5027 int NewHiScore(int level_nr)
5028 {
5029   int k, l;
5030   int position = -1;
5031   boolean one_score_entry_per_name = !program.many_scores_per_name;
5032
5033   LoadScore(level_nr);
5034
5035   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5036       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5037     return -1;
5038
5039   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5040   {
5041     if (game.score_final > highscore[k].Score)
5042     {
5043       // player has made it to the hall of fame
5044
5045       if (k < MAX_SCORE_ENTRIES - 1)
5046       {
5047         int m = MAX_SCORE_ENTRIES - 1;
5048
5049         if (one_score_entry_per_name)
5050         {
5051           for (l = k; l < MAX_SCORE_ENTRIES; l++)
5052             if (strEqual(setup.player_name, highscore[l].Name))
5053               m = l;
5054
5055           if (m == k)   // player's new highscore overwrites his old one
5056             goto put_into_list;
5057         }
5058
5059         for (l = m; l > k; l--)
5060         {
5061           strcpy(highscore[l].Name, highscore[l - 1].Name);
5062           highscore[l].Score = highscore[l - 1].Score;
5063         }
5064       }
5065
5066       put_into_list:
5067
5068       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5069       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5070       highscore[k].Score = game.score_final;
5071       position = k;
5072
5073       break;
5074     }
5075     else if (one_score_entry_per_name &&
5076              !strncmp(setup.player_name, highscore[k].Name,
5077                       MAX_PLAYER_NAME_LEN))
5078       break;    // player already there with a higher score
5079   }
5080
5081   if (position >= 0) 
5082     SaveScore(level_nr);
5083
5084   return position;
5085 }
5086
5087 static int getElementMoveStepsizeExt(int x, int y, int direction)
5088 {
5089   int element = Tile[x][y];
5090   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5091   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5092   int horiz_move = (dx != 0);
5093   int sign = (horiz_move ? dx : dy);
5094   int step = sign * element_info[element].move_stepsize;
5095
5096   // special values for move stepsize for spring and things on conveyor belt
5097   if (horiz_move)
5098   {
5099     if (CAN_FALL(element) &&
5100         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5101       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5102     else if (element == EL_SPRING)
5103       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5104   }
5105
5106   return step;
5107 }
5108
5109 static int getElementMoveStepsize(int x, int y)
5110 {
5111   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5112 }
5113
5114 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5115 {
5116   if (player->GfxAction != action || player->GfxDir != dir)
5117   {
5118     player->GfxAction = action;
5119     player->GfxDir = dir;
5120     player->Frame = 0;
5121     player->StepFrame = 0;
5122   }
5123 }
5124
5125 static void ResetGfxFrame(int x, int y)
5126 {
5127   // profiling showed that "autotest" spends 10~20% of its time in this function
5128   if (DrawingDeactivatedField())
5129     return;
5130
5131   int element = Tile[x][y];
5132   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5133
5134   if (graphic_info[graphic].anim_global_sync)
5135     GfxFrame[x][y] = FrameCounter;
5136   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5137     GfxFrame[x][y] = CustomValue[x][y];
5138   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5139     GfxFrame[x][y] = element_info[element].collect_score;
5140   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5141     GfxFrame[x][y] = ChangeDelay[x][y];
5142 }
5143
5144 static void ResetGfxAnimation(int x, int y)
5145 {
5146   GfxAction[x][y] = ACTION_DEFAULT;
5147   GfxDir[x][y] = MovDir[x][y];
5148   GfxFrame[x][y] = 0;
5149
5150   ResetGfxFrame(x, y);
5151 }
5152
5153 static void ResetRandomAnimationValue(int x, int y)
5154 {
5155   GfxRandom[x][y] = INIT_GFX_RANDOM();
5156 }
5157
5158 static void InitMovingField(int x, int y, int direction)
5159 {
5160   int element = Tile[x][y];
5161   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5162   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5163   int newx = x + dx;
5164   int newy = y + dy;
5165   boolean is_moving_before, is_moving_after;
5166
5167   // check if element was/is moving or being moved before/after mode change
5168   is_moving_before = (WasJustMoving[x][y] != 0);
5169   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5170
5171   // reset animation only for moving elements which change direction of moving
5172   // or which just started or stopped moving
5173   // (else CEs with property "can move" / "not moving" are reset each frame)
5174   if (is_moving_before != is_moving_after ||
5175       direction != MovDir[x][y])
5176     ResetGfxAnimation(x, y);
5177
5178   MovDir[x][y] = direction;
5179   GfxDir[x][y] = direction;
5180
5181   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5182                      direction == MV_DOWN && CAN_FALL(element) ?
5183                      ACTION_FALLING : ACTION_MOVING);
5184
5185   // this is needed for CEs with property "can move" / "not moving"
5186
5187   if (is_moving_after)
5188   {
5189     if (Tile[newx][newy] == EL_EMPTY)
5190       Tile[newx][newy] = EL_BLOCKED;
5191
5192     MovDir[newx][newy] = MovDir[x][y];
5193
5194     CustomValue[newx][newy] = CustomValue[x][y];
5195
5196     GfxFrame[newx][newy] = GfxFrame[x][y];
5197     GfxRandom[newx][newy] = GfxRandom[x][y];
5198     GfxAction[newx][newy] = GfxAction[x][y];
5199     GfxDir[newx][newy] = GfxDir[x][y];
5200   }
5201 }
5202
5203 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5204 {
5205   int direction = MovDir[x][y];
5206   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5207   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5208
5209   *goes_to_x = newx;
5210   *goes_to_y = newy;
5211 }
5212
5213 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5214 {
5215   int oldx = x, oldy = y;
5216   int direction = MovDir[x][y];
5217
5218   if (direction == MV_LEFT)
5219     oldx++;
5220   else if (direction == MV_RIGHT)
5221     oldx--;
5222   else if (direction == MV_UP)
5223     oldy++;
5224   else if (direction == MV_DOWN)
5225     oldy--;
5226
5227   *comes_from_x = oldx;
5228   *comes_from_y = oldy;
5229 }
5230
5231 static int MovingOrBlocked2Element(int x, int y)
5232 {
5233   int element = Tile[x][y];
5234
5235   if (element == EL_BLOCKED)
5236   {
5237     int oldx, oldy;
5238
5239     Blocked2Moving(x, y, &oldx, &oldy);
5240     return Tile[oldx][oldy];
5241   }
5242   else
5243     return element;
5244 }
5245
5246 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5247 {
5248   // like MovingOrBlocked2Element(), but if element is moving
5249   // and (x,y) is the field the moving element is just leaving,
5250   // return EL_BLOCKED instead of the element value
5251   int element = Tile[x][y];
5252
5253   if (IS_MOVING(x, y))
5254   {
5255     if (element == EL_BLOCKED)
5256     {
5257       int oldx, oldy;
5258
5259       Blocked2Moving(x, y, &oldx, &oldy);
5260       return Tile[oldx][oldy];
5261     }
5262     else
5263       return EL_BLOCKED;
5264   }
5265   else
5266     return element;
5267 }
5268
5269 static void RemoveField(int x, int y)
5270 {
5271   Tile[x][y] = EL_EMPTY;
5272
5273   MovPos[x][y] = 0;
5274   MovDir[x][y] = 0;
5275   MovDelay[x][y] = 0;
5276
5277   CustomValue[x][y] = 0;
5278
5279   AmoebaNr[x][y] = 0;
5280   ChangeDelay[x][y] = 0;
5281   ChangePage[x][y] = -1;
5282   Pushed[x][y] = FALSE;
5283
5284   GfxElement[x][y] = EL_UNDEFINED;
5285   GfxAction[x][y] = ACTION_DEFAULT;
5286   GfxDir[x][y] = MV_NONE;
5287 }
5288
5289 static void RemoveMovingField(int x, int y)
5290 {
5291   int oldx = x, oldy = y, newx = x, newy = y;
5292   int element = Tile[x][y];
5293   int next_element = EL_UNDEFINED;
5294
5295   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5296     return;
5297
5298   if (IS_MOVING(x, y))
5299   {
5300     Moving2Blocked(x, y, &newx, &newy);
5301
5302     if (Tile[newx][newy] != EL_BLOCKED)
5303     {
5304       // element is moving, but target field is not free (blocked), but
5305       // already occupied by something different (example: acid pool);
5306       // in this case, only remove the moving field, but not the target
5307
5308       RemoveField(oldx, oldy);
5309
5310       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5311
5312       TEST_DrawLevelField(oldx, oldy);
5313
5314       return;
5315     }
5316   }
5317   else if (element == EL_BLOCKED)
5318   {
5319     Blocked2Moving(x, y, &oldx, &oldy);
5320     if (!IS_MOVING(oldx, oldy))
5321       return;
5322   }
5323
5324   if (element == EL_BLOCKED &&
5325       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5326        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5327        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5328        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5329        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5330        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5331     next_element = get_next_element(Tile[oldx][oldy]);
5332
5333   RemoveField(oldx, oldy);
5334   RemoveField(newx, newy);
5335
5336   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5337
5338   if (next_element != EL_UNDEFINED)
5339     Tile[oldx][oldy] = next_element;
5340
5341   TEST_DrawLevelField(oldx, oldy);
5342   TEST_DrawLevelField(newx, newy);
5343 }
5344
5345 void DrawDynamite(int x, int y)
5346 {
5347   int sx = SCREENX(x), sy = SCREENY(y);
5348   int graphic = el2img(Tile[x][y]);
5349   int frame;
5350
5351   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5352     return;
5353
5354   if (IS_WALKABLE_INSIDE(Back[x][y]))
5355     return;
5356
5357   if (Back[x][y])
5358     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5359   else if (Store[x][y])
5360     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5361
5362   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5363
5364   if (Back[x][y] || Store[x][y])
5365     DrawGraphicThruMask(sx, sy, graphic, frame);
5366   else
5367     DrawGraphic(sx, sy, graphic, frame);
5368 }
5369
5370 static void CheckDynamite(int x, int y)
5371 {
5372   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5373   {
5374     MovDelay[x][y]--;
5375
5376     if (MovDelay[x][y] != 0)
5377     {
5378       DrawDynamite(x, y);
5379       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5380
5381       return;
5382     }
5383   }
5384
5385   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5386
5387   Bang(x, y);
5388 }
5389
5390 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5391 {
5392   boolean num_checked_players = 0;
5393   int i;
5394
5395   for (i = 0; i < MAX_PLAYERS; i++)
5396   {
5397     if (stored_player[i].active)
5398     {
5399       int sx = stored_player[i].jx;
5400       int sy = stored_player[i].jy;
5401
5402       if (num_checked_players == 0)
5403       {
5404         *sx1 = *sx2 = sx;
5405         *sy1 = *sy2 = sy;
5406       }
5407       else
5408       {
5409         *sx1 = MIN(*sx1, sx);
5410         *sy1 = MIN(*sy1, sy);
5411         *sx2 = MAX(*sx2, sx);
5412         *sy2 = MAX(*sy2, sy);
5413       }
5414
5415       num_checked_players++;
5416     }
5417   }
5418 }
5419
5420 static boolean checkIfAllPlayersFitToScreen_RND(void)
5421 {
5422   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5423
5424   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5425
5426   return (sx2 - sx1 < SCR_FIELDX &&
5427           sy2 - sy1 < SCR_FIELDY);
5428 }
5429
5430 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5431 {
5432   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5433
5434   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5435
5436   *sx = (sx1 + sx2) / 2;
5437   *sy = (sy1 + sy2) / 2;
5438 }
5439
5440 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5441                                boolean center_screen, boolean quick_relocation)
5442 {
5443   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5444   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5445   boolean no_delay = (tape.warp_forward);
5446   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5447   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5448   int new_scroll_x, new_scroll_y;
5449
5450   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5451   {
5452     // case 1: quick relocation inside visible screen (without scrolling)
5453
5454     RedrawPlayfield();
5455
5456     return;
5457   }
5458
5459   if (!level.shifted_relocation || center_screen)
5460   {
5461     // relocation _with_ centering of screen
5462
5463     new_scroll_x = SCROLL_POSITION_X(x);
5464     new_scroll_y = SCROLL_POSITION_Y(y);
5465   }
5466   else
5467   {
5468     // relocation _without_ centering of screen
5469
5470     int center_scroll_x = SCROLL_POSITION_X(old_x);
5471     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5472     int offset_x = x + (scroll_x - center_scroll_x);
5473     int offset_y = y + (scroll_y - center_scroll_y);
5474
5475     // for new screen position, apply previous offset to center position
5476     new_scroll_x = SCROLL_POSITION_X(offset_x);
5477     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5478   }
5479
5480   if (quick_relocation)
5481   {
5482     // case 2: quick relocation (redraw without visible scrolling)
5483
5484     scroll_x = new_scroll_x;
5485     scroll_y = new_scroll_y;
5486
5487     RedrawPlayfield();
5488
5489     return;
5490   }
5491
5492   // case 3: visible relocation (with scrolling to new position)
5493
5494   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5495
5496   SetVideoFrameDelay(wait_delay_value);
5497
5498   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5499   {
5500     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5501     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5502
5503     if (dx == 0 && dy == 0)             // no scrolling needed at all
5504       break;
5505
5506     scroll_x -= dx;
5507     scroll_y -= dy;
5508
5509     // set values for horizontal/vertical screen scrolling (half tile size)
5510     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5511     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5512     int pos_x = dx * TILEX / 2;
5513     int pos_y = dy * TILEY / 2;
5514     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5515     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5516
5517     ScrollLevel(dx, dy);
5518     DrawAllPlayers();
5519
5520     // scroll in two steps of half tile size to make things smoother
5521     BlitScreenToBitmapExt_RND(window, fx, fy);
5522
5523     // scroll second step to align at full tile size
5524     BlitScreenToBitmap(window);
5525   }
5526
5527   DrawAllPlayers();
5528   BackToFront();
5529
5530   SetVideoFrameDelay(frame_delay_value_old);
5531 }
5532
5533 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5534 {
5535   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5536   int player_nr = GET_PLAYER_NR(el_player);
5537   struct PlayerInfo *player = &stored_player[player_nr];
5538   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5539   boolean no_delay = (tape.warp_forward);
5540   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5541   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5542   int old_jx = player->jx;
5543   int old_jy = player->jy;
5544   int old_element = Tile[old_jx][old_jy];
5545   int element = Tile[jx][jy];
5546   boolean player_relocated = (old_jx != jx || old_jy != jy);
5547
5548   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5549   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5550   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5551   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5552   int leave_side_horiz = move_dir_horiz;
5553   int leave_side_vert  = move_dir_vert;
5554   int enter_side = enter_side_horiz | enter_side_vert;
5555   int leave_side = leave_side_horiz | leave_side_vert;
5556
5557   if (player->buried)           // do not reanimate dead player
5558     return;
5559
5560   if (!player_relocated)        // no need to relocate the player
5561     return;
5562
5563   if (IS_PLAYER(jx, jy))        // player already placed at new position
5564   {
5565     RemoveField(jx, jy);        // temporarily remove newly placed player
5566     DrawLevelField(jx, jy);
5567   }
5568
5569   if (player->present)
5570   {
5571     while (player->MovPos)
5572     {
5573       ScrollPlayer(player, SCROLL_GO_ON);
5574       ScrollScreen(NULL, SCROLL_GO_ON);
5575
5576       AdvanceFrameAndPlayerCounters(player->index_nr);
5577
5578       DrawPlayer(player);
5579
5580       BackToFront_WithFrameDelay(wait_delay_value);
5581     }
5582
5583     DrawPlayer(player);         // needed here only to cleanup last field
5584     DrawLevelField(player->jx, player->jy);     // remove player graphic
5585
5586     player->is_moving = FALSE;
5587   }
5588
5589   if (IS_CUSTOM_ELEMENT(old_element))
5590     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5591                                CE_LEFT_BY_PLAYER,
5592                                player->index_bit, leave_side);
5593
5594   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5595                                       CE_PLAYER_LEAVES_X,
5596                                       player->index_bit, leave_side);
5597
5598   Tile[jx][jy] = el_player;
5599   InitPlayerField(jx, jy, el_player, TRUE);
5600
5601   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5602      possible that the relocation target field did not contain a player element,
5603      but a walkable element, to which the new player was relocated -- in this
5604      case, restore that (already initialized!) element on the player field */
5605   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5606   {
5607     Tile[jx][jy] = element;     // restore previously existing element
5608   }
5609
5610   // only visually relocate centered player
5611   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5612                      FALSE, level.instant_relocation);
5613
5614   TestIfPlayerTouchesBadThing(jx, jy);
5615   TestIfPlayerTouchesCustomElement(jx, jy);
5616
5617   if (IS_CUSTOM_ELEMENT(element))
5618     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5619                                player->index_bit, enter_side);
5620
5621   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5622                                       player->index_bit, enter_side);
5623
5624   if (player->is_switching)
5625   {
5626     /* ensure that relocation while still switching an element does not cause
5627        a new element to be treated as also switched directly after relocation
5628        (this is important for teleporter switches that teleport the player to
5629        a place where another teleporter switch is in the same direction, which
5630        would then incorrectly be treated as immediately switched before the
5631        direction key that caused the switch was released) */
5632
5633     player->switch_x += jx - old_jx;
5634     player->switch_y += jy - old_jy;
5635   }
5636 }
5637
5638 static void Explode(int ex, int ey, int phase, int mode)
5639 {
5640   int x, y;
5641   int last_phase;
5642   int border_element;
5643
5644   // !!! eliminate this variable !!!
5645   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5646
5647   if (game.explosions_delayed)
5648   {
5649     ExplodeField[ex][ey] = mode;
5650     return;
5651   }
5652
5653   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5654   {
5655     int center_element = Tile[ex][ey];
5656     int artwork_element, explosion_element;     // set these values later
5657
5658     // remove things displayed in background while burning dynamite
5659     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5660       Back[ex][ey] = 0;
5661
5662     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5663     {
5664       // put moving element to center field (and let it explode there)
5665       center_element = MovingOrBlocked2Element(ex, ey);
5666       RemoveMovingField(ex, ey);
5667       Tile[ex][ey] = center_element;
5668     }
5669
5670     // now "center_element" is finally determined -- set related values now
5671     artwork_element = center_element;           // for custom player artwork
5672     explosion_element = center_element;         // for custom player artwork
5673
5674     if (IS_PLAYER(ex, ey))
5675     {
5676       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5677
5678       artwork_element = stored_player[player_nr].artwork_element;
5679
5680       if (level.use_explosion_element[player_nr])
5681       {
5682         explosion_element = level.explosion_element[player_nr];
5683         artwork_element = explosion_element;
5684       }
5685     }
5686
5687     if (mode == EX_TYPE_NORMAL ||
5688         mode == EX_TYPE_CENTER ||
5689         mode == EX_TYPE_CROSS)
5690       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5691
5692     last_phase = element_info[explosion_element].explosion_delay + 1;
5693
5694     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5695     {
5696       int xx = x - ex + 1;
5697       int yy = y - ey + 1;
5698       int element;
5699
5700       if (!IN_LEV_FIELD(x, y) ||
5701           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5702           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5703         continue;
5704
5705       element = Tile[x][y];
5706
5707       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5708       {
5709         element = MovingOrBlocked2Element(x, y);
5710
5711         if (!IS_EXPLOSION_PROOF(element))
5712           RemoveMovingField(x, y);
5713       }
5714
5715       // indestructible elements can only explode in center (but not flames)
5716       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5717                                            mode == EX_TYPE_BORDER)) ||
5718           element == EL_FLAMES)
5719         continue;
5720
5721       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5722          behaviour, for example when touching a yamyam that explodes to rocks
5723          with active deadly shield, a rock is created under the player !!! */
5724       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5725 #if 0
5726       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5727           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5728            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5729 #else
5730       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5731 #endif
5732       {
5733         if (IS_ACTIVE_BOMB(element))
5734         {
5735           // re-activate things under the bomb like gate or penguin
5736           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5737           Back[x][y] = 0;
5738         }
5739
5740         continue;
5741       }
5742
5743       // save walkable background elements while explosion on same tile
5744       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5745           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5746         Back[x][y] = element;
5747
5748       // ignite explodable elements reached by other explosion
5749       if (element == EL_EXPLOSION)
5750         element = Store2[x][y];
5751
5752       if (AmoebaNr[x][y] &&
5753           (element == EL_AMOEBA_FULL ||
5754            element == EL_BD_AMOEBA ||
5755            element == EL_AMOEBA_GROWING))
5756       {
5757         AmoebaCnt[AmoebaNr[x][y]]--;
5758         AmoebaCnt2[AmoebaNr[x][y]]--;
5759       }
5760
5761       RemoveField(x, y);
5762
5763       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5764       {
5765         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5766
5767         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5768
5769         if (PLAYERINFO(ex, ey)->use_murphy)
5770           Store[x][y] = EL_EMPTY;
5771       }
5772
5773       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5774       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5775       else if (ELEM_IS_PLAYER(center_element))
5776         Store[x][y] = EL_EMPTY;
5777       else if (center_element == EL_YAMYAM)
5778         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5779       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5780         Store[x][y] = element_info[center_element].content.e[xx][yy];
5781 #if 1
5782       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5783       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5784       // otherwise) -- FIX THIS !!!
5785       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5786         Store[x][y] = element_info[element].content.e[1][1];
5787 #else
5788       else if (!CAN_EXPLODE(element))
5789         Store[x][y] = element_info[element].content.e[1][1];
5790 #endif
5791       else
5792         Store[x][y] = EL_EMPTY;
5793
5794       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5795           center_element == EL_AMOEBA_TO_DIAMOND)
5796         Store2[x][y] = element;
5797
5798       Tile[x][y] = EL_EXPLOSION;
5799       GfxElement[x][y] = artwork_element;
5800
5801       ExplodePhase[x][y] = 1;
5802       ExplodeDelay[x][y] = last_phase;
5803
5804       Stop[x][y] = TRUE;
5805     }
5806
5807     if (center_element == EL_YAMYAM)
5808       game.yamyam_content_nr =
5809         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5810
5811     return;
5812   }
5813
5814   if (Stop[ex][ey])
5815     return;
5816
5817   x = ex;
5818   y = ey;
5819
5820   if (phase == 1)
5821     GfxFrame[x][y] = 0;         // restart explosion animation
5822
5823   last_phase = ExplodeDelay[x][y];
5824
5825   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5826
5827   // this can happen if the player leaves an explosion just in time
5828   if (GfxElement[x][y] == EL_UNDEFINED)
5829     GfxElement[x][y] = EL_EMPTY;
5830
5831   border_element = Store2[x][y];
5832   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5833     border_element = StorePlayer[x][y];
5834
5835   if (phase == element_info[border_element].ignition_delay ||
5836       phase == last_phase)
5837   {
5838     boolean border_explosion = FALSE;
5839
5840     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5841         !PLAYER_EXPLOSION_PROTECTED(x, y))
5842     {
5843       KillPlayerUnlessExplosionProtected(x, y);
5844       border_explosion = TRUE;
5845     }
5846     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5847     {
5848       Tile[x][y] = Store2[x][y];
5849       Store2[x][y] = 0;
5850       Bang(x, y);
5851       border_explosion = TRUE;
5852     }
5853     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5854     {
5855       AmoebaToDiamond(x, y);
5856       Store2[x][y] = 0;
5857       border_explosion = TRUE;
5858     }
5859
5860     // if an element just explodes due to another explosion (chain-reaction),
5861     // do not immediately end the new explosion when it was the last frame of
5862     // the explosion (as it would be done in the following "if"-statement!)
5863     if (border_explosion && phase == last_phase)
5864       return;
5865   }
5866
5867   if (phase == last_phase)
5868   {
5869     int element;
5870
5871     element = Tile[x][y] = Store[x][y];
5872     Store[x][y] = Store2[x][y] = 0;
5873     GfxElement[x][y] = EL_UNDEFINED;
5874
5875     // player can escape from explosions and might therefore be still alive
5876     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5877         element <= EL_PLAYER_IS_EXPLODING_4)
5878     {
5879       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5880       int explosion_element = EL_PLAYER_1 + player_nr;
5881       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5882       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5883
5884       if (level.use_explosion_element[player_nr])
5885         explosion_element = level.explosion_element[player_nr];
5886
5887       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5888                     element_info[explosion_element].content.e[xx][yy]);
5889     }
5890
5891     // restore probably existing indestructible background element
5892     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5893       element = Tile[x][y] = Back[x][y];
5894     Back[x][y] = 0;
5895
5896     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5897     GfxDir[x][y] = MV_NONE;
5898     ChangeDelay[x][y] = 0;
5899     ChangePage[x][y] = -1;
5900
5901     CustomValue[x][y] = 0;
5902
5903     InitField_WithBug2(x, y, FALSE);
5904
5905     TEST_DrawLevelField(x, y);
5906
5907     TestIfElementTouchesCustomElement(x, y);
5908
5909     if (GFX_CRUMBLED(element))
5910       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5911
5912     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5913       StorePlayer[x][y] = 0;
5914
5915     if (ELEM_IS_PLAYER(element))
5916       RelocatePlayer(x, y, element);
5917   }
5918   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5919   {
5920     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5921     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5922
5923     if (phase == delay)
5924       TEST_DrawLevelFieldCrumbled(x, y);
5925
5926     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5927     {
5928       DrawLevelElement(x, y, Back[x][y]);
5929       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5930     }
5931     else if (IS_WALKABLE_UNDER(Back[x][y]))
5932     {
5933       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5934       DrawLevelElementThruMask(x, y, Back[x][y]);
5935     }
5936     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5937       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5938   }
5939 }
5940
5941 static void DynaExplode(int ex, int ey)
5942 {
5943   int i, j;
5944   int dynabomb_element = Tile[ex][ey];
5945   int dynabomb_size = 1;
5946   boolean dynabomb_xl = FALSE;
5947   struct PlayerInfo *player;
5948   static int xy[4][2] =
5949   {
5950     { 0, -1 },
5951     { -1, 0 },
5952     { +1, 0 },
5953     { 0, +1 }
5954   };
5955
5956   if (IS_ACTIVE_BOMB(dynabomb_element))
5957   {
5958     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5959     dynabomb_size = player->dynabomb_size;
5960     dynabomb_xl = player->dynabomb_xl;
5961     player->dynabombs_left++;
5962   }
5963
5964   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5965
5966   for (i = 0; i < NUM_DIRECTIONS; i++)
5967   {
5968     for (j = 1; j <= dynabomb_size; j++)
5969     {
5970       int x = ex + j * xy[i][0];
5971       int y = ey + j * xy[i][1];
5972       int element;
5973
5974       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
5975         break;
5976
5977       element = Tile[x][y];
5978
5979       // do not restart explosions of fields with active bombs
5980       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5981         continue;
5982
5983       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5984
5985       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5986           !IS_DIGGABLE(element) && !dynabomb_xl)
5987         break;
5988     }
5989   }
5990 }
5991
5992 void Bang(int x, int y)
5993 {
5994   int element = MovingOrBlocked2Element(x, y);
5995   int explosion_type = EX_TYPE_NORMAL;
5996
5997   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5998   {
5999     struct PlayerInfo *player = PLAYERINFO(x, y);
6000
6001     element = Tile[x][y] = player->initial_element;
6002
6003     if (level.use_explosion_element[player->index_nr])
6004     {
6005       int explosion_element = level.explosion_element[player->index_nr];
6006
6007       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6008         explosion_type = EX_TYPE_CROSS;
6009       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6010         explosion_type = EX_TYPE_CENTER;
6011     }
6012   }
6013
6014   switch (element)
6015   {
6016     case EL_BUG:
6017     case EL_SPACESHIP:
6018     case EL_BD_BUTTERFLY:
6019     case EL_BD_FIREFLY:
6020     case EL_YAMYAM:
6021     case EL_DARK_YAMYAM:
6022     case EL_ROBOT:
6023     case EL_PACMAN:
6024     case EL_MOLE:
6025       RaiseScoreElement(element);
6026       break;
6027
6028     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6029     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6030     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6031     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6032     case EL_DYNABOMB_INCREASE_NUMBER:
6033     case EL_DYNABOMB_INCREASE_SIZE:
6034     case EL_DYNABOMB_INCREASE_POWER:
6035       explosion_type = EX_TYPE_DYNA;
6036       break;
6037
6038     case EL_DC_LANDMINE:
6039       explosion_type = EX_TYPE_CENTER;
6040       break;
6041
6042     case EL_PENGUIN:
6043     case EL_LAMP:
6044     case EL_LAMP_ACTIVE:
6045     case EL_AMOEBA_TO_DIAMOND:
6046       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6047         explosion_type = EX_TYPE_CENTER;
6048       break;
6049
6050     default:
6051       if (element_info[element].explosion_type == EXPLODES_CROSS)
6052         explosion_type = EX_TYPE_CROSS;
6053       else if (element_info[element].explosion_type == EXPLODES_1X1)
6054         explosion_type = EX_TYPE_CENTER;
6055       break;
6056   }
6057
6058   if (explosion_type == EX_TYPE_DYNA)
6059     DynaExplode(x, y);
6060   else
6061     Explode(x, y, EX_PHASE_START, explosion_type);
6062
6063   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6064 }
6065
6066 static void SplashAcid(int x, int y)
6067 {
6068   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6069       (!IN_LEV_FIELD(x - 1, y - 2) ||
6070        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6071     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6072
6073   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6074       (!IN_LEV_FIELD(x + 1, y - 2) ||
6075        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6076     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6077
6078   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6079 }
6080
6081 static void InitBeltMovement(void)
6082 {
6083   static int belt_base_element[4] =
6084   {
6085     EL_CONVEYOR_BELT_1_LEFT,
6086     EL_CONVEYOR_BELT_2_LEFT,
6087     EL_CONVEYOR_BELT_3_LEFT,
6088     EL_CONVEYOR_BELT_4_LEFT
6089   };
6090   static int belt_base_active_element[4] =
6091   {
6092     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6093     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6094     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6095     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6096   };
6097
6098   int x, y, i, j;
6099
6100   // set frame order for belt animation graphic according to belt direction
6101   for (i = 0; i < NUM_BELTS; i++)
6102   {
6103     int belt_nr = i;
6104
6105     for (j = 0; j < NUM_BELT_PARTS; j++)
6106     {
6107       int element = belt_base_active_element[belt_nr] + j;
6108       int graphic_1 = el2img(element);
6109       int graphic_2 = el2panelimg(element);
6110
6111       if (game.belt_dir[i] == MV_LEFT)
6112       {
6113         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6114         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6115       }
6116       else
6117       {
6118         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6119         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6120       }
6121     }
6122   }
6123
6124   SCAN_PLAYFIELD(x, y)
6125   {
6126     int element = Tile[x][y];
6127
6128     for (i = 0; i < NUM_BELTS; i++)
6129     {
6130       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6131       {
6132         int e_belt_nr = getBeltNrFromBeltElement(element);
6133         int belt_nr = i;
6134
6135         if (e_belt_nr == belt_nr)
6136         {
6137           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6138
6139           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6140         }
6141       }
6142     }
6143   }
6144 }
6145
6146 static void ToggleBeltSwitch(int x, int y)
6147 {
6148   static int belt_base_element[4] =
6149   {
6150     EL_CONVEYOR_BELT_1_LEFT,
6151     EL_CONVEYOR_BELT_2_LEFT,
6152     EL_CONVEYOR_BELT_3_LEFT,
6153     EL_CONVEYOR_BELT_4_LEFT
6154   };
6155   static int belt_base_active_element[4] =
6156   {
6157     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6158     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6159     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6160     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6161   };
6162   static int belt_base_switch_element[4] =
6163   {
6164     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6165     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6166     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6167     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6168   };
6169   static int belt_move_dir[4] =
6170   {
6171     MV_LEFT,
6172     MV_NONE,
6173     MV_RIGHT,
6174     MV_NONE,
6175   };
6176
6177   int element = Tile[x][y];
6178   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6179   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6180   int belt_dir = belt_move_dir[belt_dir_nr];
6181   int xx, yy, i;
6182
6183   if (!IS_BELT_SWITCH(element))
6184     return;
6185
6186   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6187   game.belt_dir[belt_nr] = belt_dir;
6188
6189   if (belt_dir_nr == 3)
6190     belt_dir_nr = 1;
6191
6192   // set frame order for belt animation graphic according to belt direction
6193   for (i = 0; i < NUM_BELT_PARTS; i++)
6194   {
6195     int element = belt_base_active_element[belt_nr] + i;
6196     int graphic_1 = el2img(element);
6197     int graphic_2 = el2panelimg(element);
6198
6199     if (belt_dir == MV_LEFT)
6200     {
6201       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6202       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6203     }
6204     else
6205     {
6206       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6207       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6208     }
6209   }
6210
6211   SCAN_PLAYFIELD(xx, yy)
6212   {
6213     int element = Tile[xx][yy];
6214
6215     if (IS_BELT_SWITCH(element))
6216     {
6217       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6218
6219       if (e_belt_nr == belt_nr)
6220       {
6221         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6222         TEST_DrawLevelField(xx, yy);
6223       }
6224     }
6225     else if (IS_BELT(element) && belt_dir != MV_NONE)
6226     {
6227       int e_belt_nr = getBeltNrFromBeltElement(element);
6228
6229       if (e_belt_nr == belt_nr)
6230       {
6231         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6232
6233         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6234         TEST_DrawLevelField(xx, yy);
6235       }
6236     }
6237     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6238     {
6239       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6240
6241       if (e_belt_nr == belt_nr)
6242       {
6243         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6244
6245         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6246         TEST_DrawLevelField(xx, yy);
6247       }
6248     }
6249   }
6250 }
6251
6252 static void ToggleSwitchgateSwitch(int x, int y)
6253 {
6254   int xx, yy;
6255
6256   game.switchgate_pos = !game.switchgate_pos;
6257
6258   SCAN_PLAYFIELD(xx, yy)
6259   {
6260     int element = Tile[xx][yy];
6261
6262     if (element == EL_SWITCHGATE_SWITCH_UP)
6263     {
6264       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6265       TEST_DrawLevelField(xx, yy);
6266     }
6267     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6268     {
6269       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6270       TEST_DrawLevelField(xx, yy);
6271     }
6272     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6273     {
6274       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6275       TEST_DrawLevelField(xx, yy);
6276     }
6277     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6278     {
6279       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6280       TEST_DrawLevelField(xx, yy);
6281     }
6282     else if (element == EL_SWITCHGATE_OPEN ||
6283              element == EL_SWITCHGATE_OPENING)
6284     {
6285       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6286
6287       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6288     }
6289     else if (element == EL_SWITCHGATE_CLOSED ||
6290              element == EL_SWITCHGATE_CLOSING)
6291     {
6292       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6293
6294       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6295     }
6296   }
6297 }
6298
6299 static int getInvisibleActiveFromInvisibleElement(int element)
6300 {
6301   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6302           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6303           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6304           element);
6305 }
6306
6307 static int getInvisibleFromInvisibleActiveElement(int element)
6308 {
6309   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6310           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6311           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6312           element);
6313 }
6314
6315 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6316 {
6317   int x, y;
6318
6319   SCAN_PLAYFIELD(x, y)
6320   {
6321     int element = Tile[x][y];
6322
6323     if (element == EL_LIGHT_SWITCH &&
6324         game.light_time_left > 0)
6325     {
6326       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6327       TEST_DrawLevelField(x, y);
6328     }
6329     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6330              game.light_time_left == 0)
6331     {
6332       Tile[x][y] = EL_LIGHT_SWITCH;
6333       TEST_DrawLevelField(x, y);
6334     }
6335     else if (element == EL_EMC_DRIPPER &&
6336              game.light_time_left > 0)
6337     {
6338       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6339       TEST_DrawLevelField(x, y);
6340     }
6341     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6342              game.light_time_left == 0)
6343     {
6344       Tile[x][y] = EL_EMC_DRIPPER;
6345       TEST_DrawLevelField(x, y);
6346     }
6347     else if (element == EL_INVISIBLE_STEELWALL ||
6348              element == EL_INVISIBLE_WALL ||
6349              element == EL_INVISIBLE_SAND)
6350     {
6351       if (game.light_time_left > 0)
6352         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6353
6354       TEST_DrawLevelField(x, y);
6355
6356       // uncrumble neighbour fields, if needed
6357       if (element == EL_INVISIBLE_SAND)
6358         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6359     }
6360     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6361              element == EL_INVISIBLE_WALL_ACTIVE ||
6362              element == EL_INVISIBLE_SAND_ACTIVE)
6363     {
6364       if (game.light_time_left == 0)
6365         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6366
6367       TEST_DrawLevelField(x, y);
6368
6369       // re-crumble neighbour fields, if needed
6370       if (element == EL_INVISIBLE_SAND)
6371         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6372     }
6373   }
6374 }
6375
6376 static void RedrawAllInvisibleElementsForLenses(void)
6377 {
6378   int x, y;
6379
6380   SCAN_PLAYFIELD(x, y)
6381   {
6382     int element = Tile[x][y];
6383
6384     if (element == EL_EMC_DRIPPER &&
6385         game.lenses_time_left > 0)
6386     {
6387       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6388       TEST_DrawLevelField(x, y);
6389     }
6390     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6391              game.lenses_time_left == 0)
6392     {
6393       Tile[x][y] = EL_EMC_DRIPPER;
6394       TEST_DrawLevelField(x, y);
6395     }
6396     else if (element == EL_INVISIBLE_STEELWALL ||
6397              element == EL_INVISIBLE_WALL ||
6398              element == EL_INVISIBLE_SAND)
6399     {
6400       if (game.lenses_time_left > 0)
6401         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6402
6403       TEST_DrawLevelField(x, y);
6404
6405       // uncrumble neighbour fields, if needed
6406       if (element == EL_INVISIBLE_SAND)
6407         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6408     }
6409     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6410              element == EL_INVISIBLE_WALL_ACTIVE ||
6411              element == EL_INVISIBLE_SAND_ACTIVE)
6412     {
6413       if (game.lenses_time_left == 0)
6414         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6415
6416       TEST_DrawLevelField(x, y);
6417
6418       // re-crumble neighbour fields, if needed
6419       if (element == EL_INVISIBLE_SAND)
6420         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6421     }
6422   }
6423 }
6424
6425 static void RedrawAllInvisibleElementsForMagnifier(void)
6426 {
6427   int x, y;
6428
6429   SCAN_PLAYFIELD(x, y)
6430   {
6431     int element = Tile[x][y];
6432
6433     if (element == EL_EMC_FAKE_GRASS &&
6434         game.magnify_time_left > 0)
6435     {
6436       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6437       TEST_DrawLevelField(x, y);
6438     }
6439     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6440              game.magnify_time_left == 0)
6441     {
6442       Tile[x][y] = EL_EMC_FAKE_GRASS;
6443       TEST_DrawLevelField(x, y);
6444     }
6445     else if (IS_GATE_GRAY(element) &&
6446              game.magnify_time_left > 0)
6447     {
6448       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6449                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6450                     IS_EM_GATE_GRAY(element) ?
6451                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6452                     IS_EMC_GATE_GRAY(element) ?
6453                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6454                     IS_DC_GATE_GRAY(element) ?
6455                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6456                     element);
6457       TEST_DrawLevelField(x, y);
6458     }
6459     else if (IS_GATE_GRAY_ACTIVE(element) &&
6460              game.magnify_time_left == 0)
6461     {
6462       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6463                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6464                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6465                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6466                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6467                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6468                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6469                     EL_DC_GATE_WHITE_GRAY :
6470                     element);
6471       TEST_DrawLevelField(x, y);
6472     }
6473   }
6474 }
6475
6476 static void ToggleLightSwitch(int x, int y)
6477 {
6478   int element = Tile[x][y];
6479
6480   game.light_time_left =
6481     (element == EL_LIGHT_SWITCH ?
6482      level.time_light * FRAMES_PER_SECOND : 0);
6483
6484   RedrawAllLightSwitchesAndInvisibleElements();
6485 }
6486
6487 static void ActivateTimegateSwitch(int x, int y)
6488 {
6489   int xx, yy;
6490
6491   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6492
6493   SCAN_PLAYFIELD(xx, yy)
6494   {
6495     int element = Tile[xx][yy];
6496
6497     if (element == EL_TIMEGATE_CLOSED ||
6498         element == EL_TIMEGATE_CLOSING)
6499     {
6500       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6501       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6502     }
6503
6504     /*
6505     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6506     {
6507       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6508       TEST_DrawLevelField(xx, yy);
6509     }
6510     */
6511
6512   }
6513
6514   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6515                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6516 }
6517
6518 static void Impact(int x, int y)
6519 {
6520   boolean last_line = (y == lev_fieldy - 1);
6521   boolean object_hit = FALSE;
6522   boolean impact = (last_line || object_hit);
6523   int element = Tile[x][y];
6524   int smashed = EL_STEELWALL;
6525
6526   if (!last_line)       // check if element below was hit
6527   {
6528     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6529       return;
6530
6531     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6532                                          MovDir[x][y + 1] != MV_DOWN ||
6533                                          MovPos[x][y + 1] <= TILEY / 2));
6534
6535     // do not smash moving elements that left the smashed field in time
6536     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6537         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6538       object_hit = FALSE;
6539
6540 #if USE_QUICKSAND_IMPACT_BUGFIX
6541     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6542     {
6543       RemoveMovingField(x, y + 1);
6544       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6545       Tile[x][y + 2] = EL_ROCK;
6546       TEST_DrawLevelField(x, y + 2);
6547
6548       object_hit = TRUE;
6549     }
6550
6551     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6552     {
6553       RemoveMovingField(x, y + 1);
6554       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6555       Tile[x][y + 2] = EL_ROCK;
6556       TEST_DrawLevelField(x, y + 2);
6557
6558       object_hit = TRUE;
6559     }
6560 #endif
6561
6562     if (object_hit)
6563       smashed = MovingOrBlocked2Element(x, y + 1);
6564
6565     impact = (last_line || object_hit);
6566   }
6567
6568   if (!last_line && smashed == EL_ACID) // element falls into acid
6569   {
6570     SplashAcid(x, y + 1);
6571     return;
6572   }
6573
6574   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6575   // only reset graphic animation if graphic really changes after impact
6576   if (impact &&
6577       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6578   {
6579     ResetGfxAnimation(x, y);
6580     TEST_DrawLevelField(x, y);
6581   }
6582
6583   if (impact && CAN_EXPLODE_IMPACT(element))
6584   {
6585     Bang(x, y);
6586     return;
6587   }
6588   else if (impact && element == EL_PEARL &&
6589            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6590   {
6591     ResetGfxAnimation(x, y);
6592
6593     Tile[x][y] = EL_PEARL_BREAKING;
6594     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6595     return;
6596   }
6597   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6598   {
6599     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6600
6601     return;
6602   }
6603
6604   if (impact && element == EL_AMOEBA_DROP)
6605   {
6606     if (object_hit && IS_PLAYER(x, y + 1))
6607       KillPlayerUnlessEnemyProtected(x, y + 1);
6608     else if (object_hit && smashed == EL_PENGUIN)
6609       Bang(x, y + 1);
6610     else
6611     {
6612       Tile[x][y] = EL_AMOEBA_GROWING;
6613       Store[x][y] = EL_AMOEBA_WET;
6614
6615       ResetRandomAnimationValue(x, y);
6616     }
6617     return;
6618   }
6619
6620   if (object_hit)               // check which object was hit
6621   {
6622     if ((CAN_PASS_MAGIC_WALL(element) && 
6623          (smashed == EL_MAGIC_WALL ||
6624           smashed == EL_BD_MAGIC_WALL)) ||
6625         (CAN_PASS_DC_MAGIC_WALL(element) &&
6626          smashed == EL_DC_MAGIC_WALL))
6627     {
6628       int xx, yy;
6629       int activated_magic_wall =
6630         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6631          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6632          EL_DC_MAGIC_WALL_ACTIVE);
6633
6634       // activate magic wall / mill
6635       SCAN_PLAYFIELD(xx, yy)
6636       {
6637         if (Tile[xx][yy] == smashed)
6638           Tile[xx][yy] = activated_magic_wall;
6639       }
6640
6641       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6642       game.magic_wall_active = TRUE;
6643
6644       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6645                             SND_MAGIC_WALL_ACTIVATING :
6646                             smashed == EL_BD_MAGIC_WALL ?
6647                             SND_BD_MAGIC_WALL_ACTIVATING :
6648                             SND_DC_MAGIC_WALL_ACTIVATING));
6649     }
6650
6651     if (IS_PLAYER(x, y + 1))
6652     {
6653       if (CAN_SMASH_PLAYER(element))
6654       {
6655         KillPlayerUnlessEnemyProtected(x, y + 1);
6656         return;
6657       }
6658     }
6659     else if (smashed == EL_PENGUIN)
6660     {
6661       if (CAN_SMASH_PLAYER(element))
6662       {
6663         Bang(x, y + 1);
6664         return;
6665       }
6666     }
6667     else if (element == EL_BD_DIAMOND)
6668     {
6669       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6670       {
6671         Bang(x, y + 1);
6672         return;
6673       }
6674     }
6675     else if (((element == EL_SP_INFOTRON ||
6676                element == EL_SP_ZONK) &&
6677               (smashed == EL_SP_SNIKSNAK ||
6678                smashed == EL_SP_ELECTRON ||
6679                smashed == EL_SP_DISK_ORANGE)) ||
6680              (element == EL_SP_INFOTRON &&
6681               smashed == EL_SP_DISK_YELLOW))
6682     {
6683       Bang(x, y + 1);
6684       return;
6685     }
6686     else if (CAN_SMASH_EVERYTHING(element))
6687     {
6688       if (IS_CLASSIC_ENEMY(smashed) ||
6689           CAN_EXPLODE_SMASHED(smashed))
6690       {
6691         Bang(x, y + 1);
6692         return;
6693       }
6694       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6695       {
6696         if (smashed == EL_LAMP ||
6697             smashed == EL_LAMP_ACTIVE)
6698         {
6699           Bang(x, y + 1);
6700           return;
6701         }
6702         else if (smashed == EL_NUT)
6703         {
6704           Tile[x][y + 1] = EL_NUT_BREAKING;
6705           PlayLevelSound(x, y, SND_NUT_BREAKING);
6706           RaiseScoreElement(EL_NUT);
6707           return;
6708         }
6709         else if (smashed == EL_PEARL)
6710         {
6711           ResetGfxAnimation(x, y);
6712
6713           Tile[x][y + 1] = EL_PEARL_BREAKING;
6714           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6715           return;
6716         }
6717         else if (smashed == EL_DIAMOND)
6718         {
6719           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6720           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6721           return;
6722         }
6723         else if (IS_BELT_SWITCH(smashed))
6724         {
6725           ToggleBeltSwitch(x, y + 1);
6726         }
6727         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6728                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6729                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6730                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6731         {
6732           ToggleSwitchgateSwitch(x, y + 1);
6733         }
6734         else if (smashed == EL_LIGHT_SWITCH ||
6735                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6736         {
6737           ToggleLightSwitch(x, y + 1);
6738         }
6739         else
6740         {
6741           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6742
6743           CheckElementChangeBySide(x, y + 1, smashed, element,
6744                                    CE_SWITCHED, CH_SIDE_TOP);
6745           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6746                                             CH_SIDE_TOP);
6747         }
6748       }
6749       else
6750       {
6751         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6752       }
6753     }
6754   }
6755
6756   // play sound of magic wall / mill
6757   if (!last_line &&
6758       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6759        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6760        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6761   {
6762     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6763       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6764     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6765       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6766     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6767       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6768
6769     return;
6770   }
6771
6772   // play sound of object that hits the ground
6773   if (last_line || object_hit)
6774     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6775 }
6776
6777 static void TurnRoundExt(int x, int y)
6778 {
6779   static struct
6780   {
6781     int dx, dy;
6782   } move_xy[] =
6783   {
6784     {  0,  0 },
6785     { -1,  0 },
6786     { +1,  0 },
6787     {  0,  0 },
6788     {  0, -1 },
6789     {  0,  0 }, { 0, 0 }, { 0, 0 },
6790     {  0, +1 }
6791   };
6792   static struct
6793   {
6794     int left, right, back;
6795   } turn[] =
6796   {
6797     { 0,        0,              0        },
6798     { MV_DOWN,  MV_UP,          MV_RIGHT },
6799     { MV_UP,    MV_DOWN,        MV_LEFT  },
6800     { 0,        0,              0        },
6801     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6802     { 0,        0,              0        },
6803     { 0,        0,              0        },
6804     { 0,        0,              0        },
6805     { MV_RIGHT, MV_LEFT,        MV_UP    }
6806   };
6807
6808   int element = Tile[x][y];
6809   int move_pattern = element_info[element].move_pattern;
6810
6811   int old_move_dir = MovDir[x][y];
6812   int left_dir  = turn[old_move_dir].left;
6813   int right_dir = turn[old_move_dir].right;
6814   int back_dir  = turn[old_move_dir].back;
6815
6816   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6817   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6818   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6819   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6820
6821   int left_x  = x + left_dx,  left_y  = y + left_dy;
6822   int right_x = x + right_dx, right_y = y + right_dy;
6823   int move_x  = x + move_dx,  move_y  = y + move_dy;
6824
6825   int xx, yy;
6826
6827   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6828   {
6829     TestIfBadThingTouchesOtherBadThing(x, y);
6830
6831     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6832       MovDir[x][y] = right_dir;
6833     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6834       MovDir[x][y] = left_dir;
6835
6836     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6837       MovDelay[x][y] = 9;
6838     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6839       MovDelay[x][y] = 1;
6840   }
6841   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6842   {
6843     TestIfBadThingTouchesOtherBadThing(x, y);
6844
6845     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6846       MovDir[x][y] = left_dir;
6847     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6848       MovDir[x][y] = right_dir;
6849
6850     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6851       MovDelay[x][y] = 9;
6852     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6853       MovDelay[x][y] = 1;
6854   }
6855   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6856   {
6857     TestIfBadThingTouchesOtherBadThing(x, y);
6858
6859     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6860       MovDir[x][y] = left_dir;
6861     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6862       MovDir[x][y] = right_dir;
6863
6864     if (MovDir[x][y] != old_move_dir)
6865       MovDelay[x][y] = 9;
6866   }
6867   else if (element == EL_YAMYAM)
6868   {
6869     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6870     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6871
6872     if (can_turn_left && can_turn_right)
6873       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6874     else if (can_turn_left)
6875       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6876     else if (can_turn_right)
6877       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6878     else
6879       MovDir[x][y] = back_dir;
6880
6881     MovDelay[x][y] = 16 + 16 * RND(3);
6882   }
6883   else if (element == EL_DARK_YAMYAM)
6884   {
6885     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6886                                                          left_x, left_y);
6887     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6888                                                          right_x, right_y);
6889
6890     if (can_turn_left && can_turn_right)
6891       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6892     else if (can_turn_left)
6893       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6894     else if (can_turn_right)
6895       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6896     else
6897       MovDir[x][y] = back_dir;
6898
6899     MovDelay[x][y] = 16 + 16 * RND(3);
6900   }
6901   else if (element == EL_PACMAN)
6902   {
6903     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6904     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6905
6906     if (can_turn_left && can_turn_right)
6907       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6908     else if (can_turn_left)
6909       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6910     else if (can_turn_right)
6911       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6912     else
6913       MovDir[x][y] = back_dir;
6914
6915     MovDelay[x][y] = 6 + RND(40);
6916   }
6917   else if (element == EL_PIG)
6918   {
6919     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6920     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6921     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6922     boolean should_turn_left, should_turn_right, should_move_on;
6923     int rnd_value = 24;
6924     int rnd = RND(rnd_value);
6925
6926     should_turn_left = (can_turn_left &&
6927                         (!can_move_on ||
6928                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6929                                                    y + back_dy + left_dy)));
6930     should_turn_right = (can_turn_right &&
6931                          (!can_move_on ||
6932                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6933                                                     y + back_dy + right_dy)));
6934     should_move_on = (can_move_on &&
6935                       (!can_turn_left ||
6936                        !can_turn_right ||
6937                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6938                                                  y + move_dy + left_dy) ||
6939                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6940                                                  y + move_dy + right_dy)));
6941
6942     if (should_turn_left || should_turn_right || should_move_on)
6943     {
6944       if (should_turn_left && should_turn_right && should_move_on)
6945         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6946                         rnd < 2 * rnd_value / 3 ? right_dir :
6947                         old_move_dir);
6948       else if (should_turn_left && should_turn_right)
6949         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6950       else if (should_turn_left && should_move_on)
6951         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6952       else if (should_turn_right && should_move_on)
6953         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6954       else if (should_turn_left)
6955         MovDir[x][y] = left_dir;
6956       else if (should_turn_right)
6957         MovDir[x][y] = right_dir;
6958       else if (should_move_on)
6959         MovDir[x][y] = old_move_dir;
6960     }
6961     else if (can_move_on && rnd > rnd_value / 8)
6962       MovDir[x][y] = old_move_dir;
6963     else if (can_turn_left && can_turn_right)
6964       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6965     else if (can_turn_left && rnd > rnd_value / 8)
6966       MovDir[x][y] = left_dir;
6967     else if (can_turn_right && rnd > rnd_value/8)
6968       MovDir[x][y] = right_dir;
6969     else
6970       MovDir[x][y] = back_dir;
6971
6972     xx = x + move_xy[MovDir[x][y]].dx;
6973     yy = y + move_xy[MovDir[x][y]].dy;
6974
6975     if (!IN_LEV_FIELD(xx, yy) ||
6976         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
6977       MovDir[x][y] = old_move_dir;
6978
6979     MovDelay[x][y] = 0;
6980   }
6981   else if (element == EL_DRAGON)
6982   {
6983     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6984     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6985     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6986     int rnd_value = 24;
6987     int rnd = RND(rnd_value);
6988
6989     if (can_move_on && rnd > rnd_value / 8)
6990       MovDir[x][y] = old_move_dir;
6991     else if (can_turn_left && can_turn_right)
6992       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6993     else if (can_turn_left && rnd > rnd_value / 8)
6994       MovDir[x][y] = left_dir;
6995     else if (can_turn_right && rnd > rnd_value / 8)
6996       MovDir[x][y] = right_dir;
6997     else
6998       MovDir[x][y] = back_dir;
6999
7000     xx = x + move_xy[MovDir[x][y]].dx;
7001     yy = y + move_xy[MovDir[x][y]].dy;
7002
7003     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7004       MovDir[x][y] = old_move_dir;
7005
7006     MovDelay[x][y] = 0;
7007   }
7008   else if (element == EL_MOLE)
7009   {
7010     boolean can_move_on =
7011       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7012                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7013                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7014     if (!can_move_on)
7015     {
7016       boolean can_turn_left =
7017         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7018                               IS_AMOEBOID(Tile[left_x][left_y])));
7019
7020       boolean can_turn_right =
7021         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7022                               IS_AMOEBOID(Tile[right_x][right_y])));
7023
7024       if (can_turn_left && can_turn_right)
7025         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7026       else if (can_turn_left)
7027         MovDir[x][y] = left_dir;
7028       else
7029         MovDir[x][y] = right_dir;
7030     }
7031
7032     if (MovDir[x][y] != old_move_dir)
7033       MovDelay[x][y] = 9;
7034   }
7035   else if (element == EL_BALLOON)
7036   {
7037     MovDir[x][y] = game.wind_direction;
7038     MovDelay[x][y] = 0;
7039   }
7040   else if (element == EL_SPRING)
7041   {
7042     if (MovDir[x][y] & MV_HORIZONTAL)
7043     {
7044       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7045           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7046       {
7047         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7048         ResetGfxAnimation(move_x, move_y);
7049         TEST_DrawLevelField(move_x, move_y);
7050
7051         MovDir[x][y] = back_dir;
7052       }
7053       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7054                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7055         MovDir[x][y] = MV_NONE;
7056     }
7057
7058     MovDelay[x][y] = 0;
7059   }
7060   else if (element == EL_ROBOT ||
7061            element == EL_SATELLITE ||
7062            element == EL_PENGUIN ||
7063            element == EL_EMC_ANDROID)
7064   {
7065     int attr_x = -1, attr_y = -1;
7066
7067     if (game.all_players_gone)
7068     {
7069       attr_x = game.exit_x;
7070       attr_y = game.exit_y;
7071     }
7072     else
7073     {
7074       int i;
7075
7076       for (i = 0; i < MAX_PLAYERS; i++)
7077       {
7078         struct PlayerInfo *player = &stored_player[i];
7079         int jx = player->jx, jy = player->jy;
7080
7081         if (!player->active)
7082           continue;
7083
7084         if (attr_x == -1 ||
7085             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7086         {
7087           attr_x = jx;
7088           attr_y = jy;
7089         }
7090       }
7091     }
7092
7093     if (element == EL_ROBOT &&
7094         game.robot_wheel_x >= 0 &&
7095         game.robot_wheel_y >= 0 &&
7096         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7097          game.engine_version < VERSION_IDENT(3,1,0,0)))
7098     {
7099       attr_x = game.robot_wheel_x;
7100       attr_y = game.robot_wheel_y;
7101     }
7102
7103     if (element == EL_PENGUIN)
7104     {
7105       int i;
7106       static int xy[4][2] =
7107       {
7108         { 0, -1 },
7109         { -1, 0 },
7110         { +1, 0 },
7111         { 0, +1 }
7112       };
7113
7114       for (i = 0; i < NUM_DIRECTIONS; i++)
7115       {
7116         int ex = x + xy[i][0];
7117         int ey = y + xy[i][1];
7118
7119         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7120                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7121                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7122                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7123         {
7124           attr_x = ex;
7125           attr_y = ey;
7126           break;
7127         }
7128       }
7129     }
7130
7131     MovDir[x][y] = MV_NONE;
7132     if (attr_x < x)
7133       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7134     else if (attr_x > x)
7135       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7136     if (attr_y < y)
7137       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7138     else if (attr_y > y)
7139       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7140
7141     if (element == EL_ROBOT)
7142     {
7143       int newx, newy;
7144
7145       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7146         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7147       Moving2Blocked(x, y, &newx, &newy);
7148
7149       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7150         MovDelay[x][y] = 8 + 8 * !RND(3);
7151       else
7152         MovDelay[x][y] = 16;
7153     }
7154     else if (element == EL_PENGUIN)
7155     {
7156       int newx, newy;
7157
7158       MovDelay[x][y] = 1;
7159
7160       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7161       {
7162         boolean first_horiz = RND(2);
7163         int new_move_dir = MovDir[x][y];
7164
7165         MovDir[x][y] =
7166           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7167         Moving2Blocked(x, y, &newx, &newy);
7168
7169         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7170           return;
7171
7172         MovDir[x][y] =
7173           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7174         Moving2Blocked(x, y, &newx, &newy);
7175
7176         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7177           return;
7178
7179         MovDir[x][y] = old_move_dir;
7180         return;
7181       }
7182     }
7183     else if (element == EL_SATELLITE)
7184     {
7185       int newx, newy;
7186
7187       MovDelay[x][y] = 1;
7188
7189       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7190       {
7191         boolean first_horiz = RND(2);
7192         int new_move_dir = MovDir[x][y];
7193
7194         MovDir[x][y] =
7195           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7196         Moving2Blocked(x, y, &newx, &newy);
7197
7198         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7199           return;
7200
7201         MovDir[x][y] =
7202           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7203         Moving2Blocked(x, y, &newx, &newy);
7204
7205         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7206           return;
7207
7208         MovDir[x][y] = old_move_dir;
7209         return;
7210       }
7211     }
7212     else if (element == EL_EMC_ANDROID)
7213     {
7214       static int check_pos[16] =
7215       {
7216         -1,             //  0 => (invalid)
7217         7,              //  1 => MV_LEFT
7218         3,              //  2 => MV_RIGHT
7219         -1,             //  3 => (invalid)
7220         1,              //  4 =>            MV_UP
7221         0,              //  5 => MV_LEFT  | MV_UP
7222         2,              //  6 => MV_RIGHT | MV_UP
7223         -1,             //  7 => (invalid)
7224         5,              //  8 =>            MV_DOWN
7225         6,              //  9 => MV_LEFT  | MV_DOWN
7226         4,              // 10 => MV_RIGHT | MV_DOWN
7227         -1,             // 11 => (invalid)
7228         -1,             // 12 => (invalid)
7229         -1,             // 13 => (invalid)
7230         -1,             // 14 => (invalid)
7231         -1,             // 15 => (invalid)
7232       };
7233       static struct
7234       {
7235         int dx, dy;
7236         int dir;
7237       } check_xy[8] =
7238       {
7239         { -1, -1,       MV_LEFT  | MV_UP   },
7240         {  0, -1,                  MV_UP   },
7241         { +1, -1,       MV_RIGHT | MV_UP   },
7242         { +1,  0,       MV_RIGHT           },
7243         { +1, +1,       MV_RIGHT | MV_DOWN },
7244         {  0, +1,                  MV_DOWN },
7245         { -1, +1,       MV_LEFT  | MV_DOWN },
7246         { -1,  0,       MV_LEFT            },
7247       };
7248       int start_pos, check_order;
7249       boolean can_clone = FALSE;
7250       int i;
7251
7252       // check if there is any free field around current position
7253       for (i = 0; i < 8; i++)
7254       {
7255         int newx = x + check_xy[i].dx;
7256         int newy = y + check_xy[i].dy;
7257
7258         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7259         {
7260           can_clone = TRUE;
7261
7262           break;
7263         }
7264       }
7265
7266       if (can_clone)            // randomly find an element to clone
7267       {
7268         can_clone = FALSE;
7269
7270         start_pos = check_pos[RND(8)];
7271         check_order = (RND(2) ? -1 : +1);
7272
7273         for (i = 0; i < 8; i++)
7274         {
7275           int pos_raw = start_pos + i * check_order;
7276           int pos = (pos_raw + 8) % 8;
7277           int newx = x + check_xy[pos].dx;
7278           int newy = y + check_xy[pos].dy;
7279
7280           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7281           {
7282             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7283             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7284
7285             Store[x][y] = Tile[newx][newy];
7286
7287             can_clone = TRUE;
7288
7289             break;
7290           }
7291         }
7292       }
7293
7294       if (can_clone)            // randomly find a direction to move
7295       {
7296         can_clone = FALSE;
7297
7298         start_pos = check_pos[RND(8)];
7299         check_order = (RND(2) ? -1 : +1);
7300
7301         for (i = 0; i < 8; i++)
7302         {
7303           int pos_raw = start_pos + i * check_order;
7304           int pos = (pos_raw + 8) % 8;
7305           int newx = x + check_xy[pos].dx;
7306           int newy = y + check_xy[pos].dy;
7307           int new_move_dir = check_xy[pos].dir;
7308
7309           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7310           {
7311             MovDir[x][y] = new_move_dir;
7312             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7313
7314             can_clone = TRUE;
7315
7316             break;
7317           }
7318         }
7319       }
7320
7321       if (can_clone)            // cloning and moving successful
7322         return;
7323
7324       // cannot clone -- try to move towards player
7325
7326       start_pos = check_pos[MovDir[x][y] & 0x0f];
7327       check_order = (RND(2) ? -1 : +1);
7328
7329       for (i = 0; i < 3; i++)
7330       {
7331         // first check start_pos, then previous/next or (next/previous) pos
7332         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7333         int pos = (pos_raw + 8) % 8;
7334         int newx = x + check_xy[pos].dx;
7335         int newy = y + check_xy[pos].dy;
7336         int new_move_dir = check_xy[pos].dir;
7337
7338         if (IS_PLAYER(newx, newy))
7339           break;
7340
7341         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7342         {
7343           MovDir[x][y] = new_move_dir;
7344           MovDelay[x][y] = level.android_move_time * 8 + 1;
7345
7346           break;
7347         }
7348       }
7349     }
7350   }
7351   else if (move_pattern == MV_TURNING_LEFT ||
7352            move_pattern == MV_TURNING_RIGHT ||
7353            move_pattern == MV_TURNING_LEFT_RIGHT ||
7354            move_pattern == MV_TURNING_RIGHT_LEFT ||
7355            move_pattern == MV_TURNING_RANDOM ||
7356            move_pattern == MV_ALL_DIRECTIONS)
7357   {
7358     boolean can_turn_left =
7359       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7360     boolean can_turn_right =
7361       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7362
7363     if (element_info[element].move_stepsize == 0)       // "not moving"
7364       return;
7365
7366     if (move_pattern == MV_TURNING_LEFT)
7367       MovDir[x][y] = left_dir;
7368     else if (move_pattern == MV_TURNING_RIGHT)
7369       MovDir[x][y] = right_dir;
7370     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7371       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7372     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7373       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7374     else if (move_pattern == MV_TURNING_RANDOM)
7375       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7376                       can_turn_right && !can_turn_left ? right_dir :
7377                       RND(2) ? left_dir : right_dir);
7378     else if (can_turn_left && can_turn_right)
7379       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7380     else if (can_turn_left)
7381       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7382     else if (can_turn_right)
7383       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7384     else
7385       MovDir[x][y] = back_dir;
7386
7387     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7388   }
7389   else if (move_pattern == MV_HORIZONTAL ||
7390            move_pattern == MV_VERTICAL)
7391   {
7392     if (move_pattern & old_move_dir)
7393       MovDir[x][y] = back_dir;
7394     else if (move_pattern == MV_HORIZONTAL)
7395       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7396     else if (move_pattern == MV_VERTICAL)
7397       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7398
7399     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7400   }
7401   else if (move_pattern & MV_ANY_DIRECTION)
7402   {
7403     MovDir[x][y] = move_pattern;
7404     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7405   }
7406   else if (move_pattern & MV_WIND_DIRECTION)
7407   {
7408     MovDir[x][y] = game.wind_direction;
7409     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7410   }
7411   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7412   {
7413     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7414       MovDir[x][y] = left_dir;
7415     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7416       MovDir[x][y] = right_dir;
7417
7418     if (MovDir[x][y] != old_move_dir)
7419       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7420   }
7421   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7422   {
7423     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7424       MovDir[x][y] = right_dir;
7425     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7426       MovDir[x][y] = left_dir;
7427
7428     if (MovDir[x][y] != old_move_dir)
7429       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7430   }
7431   else if (move_pattern == MV_TOWARDS_PLAYER ||
7432            move_pattern == MV_AWAY_FROM_PLAYER)
7433   {
7434     int attr_x = -1, attr_y = -1;
7435     int newx, newy;
7436     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7437
7438     if (game.all_players_gone)
7439     {
7440       attr_x = game.exit_x;
7441       attr_y = game.exit_y;
7442     }
7443     else
7444     {
7445       int i;
7446
7447       for (i = 0; i < MAX_PLAYERS; i++)
7448       {
7449         struct PlayerInfo *player = &stored_player[i];
7450         int jx = player->jx, jy = player->jy;
7451
7452         if (!player->active)
7453           continue;
7454
7455         if (attr_x == -1 ||
7456             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7457         {
7458           attr_x = jx;
7459           attr_y = jy;
7460         }
7461       }
7462     }
7463
7464     MovDir[x][y] = MV_NONE;
7465     if (attr_x < x)
7466       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7467     else if (attr_x > x)
7468       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7469     if (attr_y < y)
7470       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7471     else if (attr_y > y)
7472       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7473
7474     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7475
7476     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7477     {
7478       boolean first_horiz = RND(2);
7479       int new_move_dir = MovDir[x][y];
7480
7481       if (element_info[element].move_stepsize == 0)     // "not moving"
7482       {
7483         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7484         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7485
7486         return;
7487       }
7488
7489       MovDir[x][y] =
7490         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7491       Moving2Blocked(x, y, &newx, &newy);
7492
7493       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7494         return;
7495
7496       MovDir[x][y] =
7497         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7498       Moving2Blocked(x, y, &newx, &newy);
7499
7500       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7501         return;
7502
7503       MovDir[x][y] = old_move_dir;
7504     }
7505   }
7506   else if (move_pattern == MV_WHEN_PUSHED ||
7507            move_pattern == MV_WHEN_DROPPED)
7508   {
7509     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7510       MovDir[x][y] = MV_NONE;
7511
7512     MovDelay[x][y] = 0;
7513   }
7514   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7515   {
7516     static int test_xy[7][2] =
7517     {
7518       { 0, -1 },
7519       { -1, 0 },
7520       { +1, 0 },
7521       { 0, +1 },
7522       { 0, -1 },
7523       { -1, 0 },
7524       { +1, 0 },
7525     };
7526     static int test_dir[7] =
7527     {
7528       MV_UP,
7529       MV_LEFT,
7530       MV_RIGHT,
7531       MV_DOWN,
7532       MV_UP,
7533       MV_LEFT,
7534       MV_RIGHT,
7535     };
7536     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7537     int move_preference = -1000000;     // start with very low preference
7538     int new_move_dir = MV_NONE;
7539     int start_test = RND(4);
7540     int i;
7541
7542     for (i = 0; i < NUM_DIRECTIONS; i++)
7543     {
7544       int move_dir = test_dir[start_test + i];
7545       int move_dir_preference;
7546
7547       xx = x + test_xy[start_test + i][0];
7548       yy = y + test_xy[start_test + i][1];
7549
7550       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7551           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7552       {
7553         new_move_dir = move_dir;
7554
7555         break;
7556       }
7557
7558       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7559         continue;
7560
7561       move_dir_preference = -1 * RunnerVisit[xx][yy];
7562       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7563         move_dir_preference = PlayerVisit[xx][yy];
7564
7565       if (move_dir_preference > move_preference)
7566       {
7567         // prefer field that has not been visited for the longest time
7568         move_preference = move_dir_preference;
7569         new_move_dir = move_dir;
7570       }
7571       else if (move_dir_preference == move_preference &&
7572                move_dir == old_move_dir)
7573       {
7574         // prefer last direction when all directions are preferred equally
7575         move_preference = move_dir_preference;
7576         new_move_dir = move_dir;
7577       }
7578     }
7579
7580     MovDir[x][y] = new_move_dir;
7581     if (old_move_dir != new_move_dir)
7582       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7583   }
7584 }
7585
7586 static void TurnRound(int x, int y)
7587 {
7588   int direction = MovDir[x][y];
7589
7590   TurnRoundExt(x, y);
7591
7592   GfxDir[x][y] = MovDir[x][y];
7593
7594   if (direction != MovDir[x][y])
7595     GfxFrame[x][y] = 0;
7596
7597   if (MovDelay[x][y])
7598     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7599
7600   ResetGfxFrame(x, y);
7601 }
7602
7603 static boolean JustBeingPushed(int x, int y)
7604 {
7605   int i;
7606
7607   for (i = 0; i < MAX_PLAYERS; i++)
7608   {
7609     struct PlayerInfo *player = &stored_player[i];
7610
7611     if (player->active && player->is_pushing && player->MovPos)
7612     {
7613       int next_jx = player->jx + (player->jx - player->last_jx);
7614       int next_jy = player->jy + (player->jy - player->last_jy);
7615
7616       if (x == next_jx && y == next_jy)
7617         return TRUE;
7618     }
7619   }
7620
7621   return FALSE;
7622 }
7623
7624 static void StartMoving(int x, int y)
7625 {
7626   boolean started_moving = FALSE;       // some elements can fall _and_ move
7627   int element = Tile[x][y];
7628
7629   if (Stop[x][y])
7630     return;
7631
7632   if (MovDelay[x][y] == 0)
7633     GfxAction[x][y] = ACTION_DEFAULT;
7634
7635   if (CAN_FALL(element) && y < lev_fieldy - 1)
7636   {
7637     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7638         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7639       if (JustBeingPushed(x, y))
7640         return;
7641
7642     if (element == EL_QUICKSAND_FULL)
7643     {
7644       if (IS_FREE(x, y + 1))
7645       {
7646         InitMovingField(x, y, MV_DOWN);
7647         started_moving = TRUE;
7648
7649         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7650 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7651         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7652           Store[x][y] = EL_ROCK;
7653 #else
7654         Store[x][y] = EL_ROCK;
7655 #endif
7656
7657         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7658       }
7659       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7660       {
7661         if (!MovDelay[x][y])
7662         {
7663           MovDelay[x][y] = TILEY + 1;
7664
7665           ResetGfxAnimation(x, y);
7666           ResetGfxAnimation(x, y + 1);
7667         }
7668
7669         if (MovDelay[x][y])
7670         {
7671           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7672           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7673
7674           MovDelay[x][y]--;
7675           if (MovDelay[x][y])
7676             return;
7677         }
7678
7679         Tile[x][y] = EL_QUICKSAND_EMPTY;
7680         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7681         Store[x][y + 1] = Store[x][y];
7682         Store[x][y] = 0;
7683
7684         PlayLevelSoundAction(x, y, ACTION_FILLING);
7685       }
7686       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7687       {
7688         if (!MovDelay[x][y])
7689         {
7690           MovDelay[x][y] = TILEY + 1;
7691
7692           ResetGfxAnimation(x, y);
7693           ResetGfxAnimation(x, y + 1);
7694         }
7695
7696         if (MovDelay[x][y])
7697         {
7698           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7699           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7700
7701           MovDelay[x][y]--;
7702           if (MovDelay[x][y])
7703             return;
7704         }
7705
7706         Tile[x][y] = EL_QUICKSAND_EMPTY;
7707         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7708         Store[x][y + 1] = Store[x][y];
7709         Store[x][y] = 0;
7710
7711         PlayLevelSoundAction(x, y, ACTION_FILLING);
7712       }
7713     }
7714     else if (element == EL_QUICKSAND_FAST_FULL)
7715     {
7716       if (IS_FREE(x, y + 1))
7717       {
7718         InitMovingField(x, y, MV_DOWN);
7719         started_moving = TRUE;
7720
7721         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7722 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7723         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7724           Store[x][y] = EL_ROCK;
7725 #else
7726         Store[x][y] = EL_ROCK;
7727 #endif
7728
7729         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7730       }
7731       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7732       {
7733         if (!MovDelay[x][y])
7734         {
7735           MovDelay[x][y] = TILEY + 1;
7736
7737           ResetGfxAnimation(x, y);
7738           ResetGfxAnimation(x, y + 1);
7739         }
7740
7741         if (MovDelay[x][y])
7742         {
7743           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7744           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7745
7746           MovDelay[x][y]--;
7747           if (MovDelay[x][y])
7748             return;
7749         }
7750
7751         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7752         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7753         Store[x][y + 1] = Store[x][y];
7754         Store[x][y] = 0;
7755
7756         PlayLevelSoundAction(x, y, ACTION_FILLING);
7757       }
7758       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7759       {
7760         if (!MovDelay[x][y])
7761         {
7762           MovDelay[x][y] = TILEY + 1;
7763
7764           ResetGfxAnimation(x, y);
7765           ResetGfxAnimation(x, y + 1);
7766         }
7767
7768         if (MovDelay[x][y])
7769         {
7770           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7771           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7772
7773           MovDelay[x][y]--;
7774           if (MovDelay[x][y])
7775             return;
7776         }
7777
7778         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7779         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7780         Store[x][y + 1] = Store[x][y];
7781         Store[x][y] = 0;
7782
7783         PlayLevelSoundAction(x, y, ACTION_FILLING);
7784       }
7785     }
7786     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7787              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7788     {
7789       InitMovingField(x, y, MV_DOWN);
7790       started_moving = TRUE;
7791
7792       Tile[x][y] = EL_QUICKSAND_FILLING;
7793       Store[x][y] = element;
7794
7795       PlayLevelSoundAction(x, y, ACTION_FILLING);
7796     }
7797     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7798              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7799     {
7800       InitMovingField(x, y, MV_DOWN);
7801       started_moving = TRUE;
7802
7803       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7804       Store[x][y] = element;
7805
7806       PlayLevelSoundAction(x, y, ACTION_FILLING);
7807     }
7808     else if (element == EL_MAGIC_WALL_FULL)
7809     {
7810       if (IS_FREE(x, y + 1))
7811       {
7812         InitMovingField(x, y, MV_DOWN);
7813         started_moving = TRUE;
7814
7815         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7816         Store[x][y] = EL_CHANGED(Store[x][y]);
7817       }
7818       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7819       {
7820         if (!MovDelay[x][y])
7821           MovDelay[x][y] = TILEY / 4 + 1;
7822
7823         if (MovDelay[x][y])
7824         {
7825           MovDelay[x][y]--;
7826           if (MovDelay[x][y])
7827             return;
7828         }
7829
7830         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7831         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7832         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7833         Store[x][y] = 0;
7834       }
7835     }
7836     else if (element == EL_BD_MAGIC_WALL_FULL)
7837     {
7838       if (IS_FREE(x, y + 1))
7839       {
7840         InitMovingField(x, y, MV_DOWN);
7841         started_moving = TRUE;
7842
7843         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7844         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7845       }
7846       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7847       {
7848         if (!MovDelay[x][y])
7849           MovDelay[x][y] = TILEY / 4 + 1;
7850
7851         if (MovDelay[x][y])
7852         {
7853           MovDelay[x][y]--;
7854           if (MovDelay[x][y])
7855             return;
7856         }
7857
7858         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7859         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7860         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7861         Store[x][y] = 0;
7862       }
7863     }
7864     else if (element == EL_DC_MAGIC_WALL_FULL)
7865     {
7866       if (IS_FREE(x, y + 1))
7867       {
7868         InitMovingField(x, y, MV_DOWN);
7869         started_moving = TRUE;
7870
7871         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7872         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7873       }
7874       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7875       {
7876         if (!MovDelay[x][y])
7877           MovDelay[x][y] = TILEY / 4 + 1;
7878
7879         if (MovDelay[x][y])
7880         {
7881           MovDelay[x][y]--;
7882           if (MovDelay[x][y])
7883             return;
7884         }
7885
7886         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7887         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7888         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7889         Store[x][y] = 0;
7890       }
7891     }
7892     else if ((CAN_PASS_MAGIC_WALL(element) &&
7893               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7894                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7895              (CAN_PASS_DC_MAGIC_WALL(element) &&
7896               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7897
7898     {
7899       InitMovingField(x, y, MV_DOWN);
7900       started_moving = TRUE;
7901
7902       Tile[x][y] =
7903         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7904          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7905          EL_DC_MAGIC_WALL_FILLING);
7906       Store[x][y] = element;
7907     }
7908     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7909     {
7910       SplashAcid(x, y + 1);
7911
7912       InitMovingField(x, y, MV_DOWN);
7913       started_moving = TRUE;
7914
7915       Store[x][y] = EL_ACID;
7916     }
7917     else if (
7918              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7919               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7920              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7921               CAN_FALL(element) && WasJustFalling[x][y] &&
7922               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7923
7924              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7925               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7926               (Tile[x][y + 1] == EL_BLOCKED)))
7927     {
7928       /* this is needed for a special case not covered by calling "Impact()"
7929          from "ContinueMoving()": if an element moves to a tile directly below
7930          another element which was just falling on that tile (which was empty
7931          in the previous frame), the falling element above would just stop
7932          instead of smashing the element below (in previous version, the above
7933          element was just checked for "moving" instead of "falling", resulting
7934          in incorrect smashes caused by horizontal movement of the above
7935          element; also, the case of the player being the element to smash was
7936          simply not covered here... :-/ ) */
7937
7938       CheckCollision[x][y] = 0;
7939       CheckImpact[x][y] = 0;
7940
7941       Impact(x, y);
7942     }
7943     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7944     {
7945       if (MovDir[x][y] == MV_NONE)
7946       {
7947         InitMovingField(x, y, MV_DOWN);
7948         started_moving = TRUE;
7949       }
7950     }
7951     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7952     {
7953       if (WasJustFalling[x][y]) // prevent animation from being restarted
7954         MovDir[x][y] = MV_DOWN;
7955
7956       InitMovingField(x, y, MV_DOWN);
7957       started_moving = TRUE;
7958     }
7959     else if (element == EL_AMOEBA_DROP)
7960     {
7961       Tile[x][y] = EL_AMOEBA_GROWING;
7962       Store[x][y] = EL_AMOEBA_WET;
7963     }
7964     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7965               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7966              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7967              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7968     {
7969       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7970                                 (IS_FREE(x - 1, y + 1) ||
7971                                  Tile[x - 1][y + 1] == EL_ACID));
7972       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7973                                 (IS_FREE(x + 1, y + 1) ||
7974                                  Tile[x + 1][y + 1] == EL_ACID));
7975       boolean can_fall_any  = (can_fall_left || can_fall_right);
7976       boolean can_fall_both = (can_fall_left && can_fall_right);
7977       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
7978
7979       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7980       {
7981         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7982           can_fall_right = FALSE;
7983         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7984           can_fall_left = FALSE;
7985         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7986           can_fall_right = FALSE;
7987         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7988           can_fall_left = FALSE;
7989
7990         can_fall_any  = (can_fall_left || can_fall_right);
7991         can_fall_both = FALSE;
7992       }
7993
7994       if (can_fall_both)
7995       {
7996         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7997           can_fall_right = FALSE;       // slip down on left side
7998         else
7999           can_fall_left = !(can_fall_right = RND(2));
8000
8001         can_fall_both = FALSE;
8002       }
8003
8004       if (can_fall_any)
8005       {
8006         // if not determined otherwise, prefer left side for slipping down
8007         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8008         started_moving = TRUE;
8009       }
8010     }
8011     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8012     {
8013       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8014       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8015       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8016       int belt_dir = game.belt_dir[belt_nr];
8017
8018       if ((belt_dir == MV_LEFT  && left_is_free) ||
8019           (belt_dir == MV_RIGHT && right_is_free))
8020       {
8021         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8022
8023         InitMovingField(x, y, belt_dir);
8024         started_moving = TRUE;
8025
8026         Pushed[x][y] = TRUE;
8027         Pushed[nextx][y] = TRUE;
8028
8029         GfxAction[x][y] = ACTION_DEFAULT;
8030       }
8031       else
8032       {
8033         MovDir[x][y] = 0;       // if element was moving, stop it
8034       }
8035     }
8036   }
8037
8038   // not "else if" because of elements that can fall and move (EL_SPRING)
8039   if (CAN_MOVE(element) && !started_moving)
8040   {
8041     int move_pattern = element_info[element].move_pattern;
8042     int newx, newy;
8043
8044     Moving2Blocked(x, y, &newx, &newy);
8045
8046     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8047       return;
8048
8049     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8050         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8051     {
8052       WasJustMoving[x][y] = 0;
8053       CheckCollision[x][y] = 0;
8054
8055       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8056
8057       if (Tile[x][y] != element)        // element has changed
8058         return;
8059     }
8060
8061     if (!MovDelay[x][y])        // start new movement phase
8062     {
8063       // all objects that can change their move direction after each step
8064       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8065
8066       if (element != EL_YAMYAM &&
8067           element != EL_DARK_YAMYAM &&
8068           element != EL_PACMAN &&
8069           !(move_pattern & MV_ANY_DIRECTION) &&
8070           move_pattern != MV_TURNING_LEFT &&
8071           move_pattern != MV_TURNING_RIGHT &&
8072           move_pattern != MV_TURNING_LEFT_RIGHT &&
8073           move_pattern != MV_TURNING_RIGHT_LEFT &&
8074           move_pattern != MV_TURNING_RANDOM)
8075       {
8076         TurnRound(x, y);
8077
8078         if (MovDelay[x][y] && (element == EL_BUG ||
8079                                element == EL_SPACESHIP ||
8080                                element == EL_SP_SNIKSNAK ||
8081                                element == EL_SP_ELECTRON ||
8082                                element == EL_MOLE))
8083           TEST_DrawLevelField(x, y);
8084       }
8085     }
8086
8087     if (MovDelay[x][y])         // wait some time before next movement
8088     {
8089       MovDelay[x][y]--;
8090
8091       if (element == EL_ROBOT ||
8092           element == EL_YAMYAM ||
8093           element == EL_DARK_YAMYAM)
8094       {
8095         DrawLevelElementAnimationIfNeeded(x, y, element);
8096         PlayLevelSoundAction(x, y, ACTION_WAITING);
8097       }
8098       else if (element == EL_SP_ELECTRON)
8099         DrawLevelElementAnimationIfNeeded(x, y, element);
8100       else if (element == EL_DRAGON)
8101       {
8102         int i;
8103         int dir = MovDir[x][y];
8104         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8105         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8106         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8107                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8108                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8109                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8110         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8111
8112         GfxAction[x][y] = ACTION_ATTACKING;
8113
8114         if (IS_PLAYER(x, y))
8115           DrawPlayerField(x, y);
8116         else
8117           TEST_DrawLevelField(x, y);
8118
8119         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8120
8121         for (i = 1; i <= 3; i++)
8122         {
8123           int xx = x + i * dx;
8124           int yy = y + i * dy;
8125           int sx = SCREENX(xx);
8126           int sy = SCREENY(yy);
8127           int flame_graphic = graphic + (i - 1);
8128
8129           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8130             break;
8131
8132           if (MovDelay[x][y])
8133           {
8134             int flamed = MovingOrBlocked2Element(xx, yy);
8135
8136             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8137               Bang(xx, yy);
8138             else
8139               RemoveMovingField(xx, yy);
8140
8141             ChangeDelay[xx][yy] = 0;
8142
8143             Tile[xx][yy] = EL_FLAMES;
8144
8145             if (IN_SCR_FIELD(sx, sy))
8146             {
8147               TEST_DrawLevelFieldCrumbled(xx, yy);
8148               DrawGraphic(sx, sy, flame_graphic, frame);
8149             }
8150           }
8151           else
8152           {
8153             if (Tile[xx][yy] == EL_FLAMES)
8154               Tile[xx][yy] = EL_EMPTY;
8155             TEST_DrawLevelField(xx, yy);
8156           }
8157         }
8158       }
8159
8160       if (MovDelay[x][y])       // element still has to wait some time
8161       {
8162         PlayLevelSoundAction(x, y, ACTION_WAITING);
8163
8164         return;
8165       }
8166     }
8167
8168     // now make next step
8169
8170     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8171
8172     if (DONT_COLLIDE_WITH(element) &&
8173         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8174         !PLAYER_ENEMY_PROTECTED(newx, newy))
8175     {
8176       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8177
8178       return;
8179     }
8180
8181     else if (CAN_MOVE_INTO_ACID(element) &&
8182              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8183              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8184              (MovDir[x][y] == MV_DOWN ||
8185               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8186     {
8187       SplashAcid(newx, newy);
8188       Store[x][y] = EL_ACID;
8189     }
8190     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8191     {
8192       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8193           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8194           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8195           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8196       {
8197         RemoveField(x, y);
8198         TEST_DrawLevelField(x, y);
8199
8200         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8201         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8202           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8203
8204         game.friends_still_needed--;
8205         if (!game.friends_still_needed &&
8206             !game.GameOver &&
8207             game.all_players_gone)
8208           LevelSolved();
8209
8210         return;
8211       }
8212       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8213       {
8214         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8215           TEST_DrawLevelField(newx, newy);
8216         else
8217           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8218       }
8219       else if (!IS_FREE(newx, newy))
8220       {
8221         GfxAction[x][y] = ACTION_WAITING;
8222
8223         if (IS_PLAYER(x, y))
8224           DrawPlayerField(x, y);
8225         else
8226           TEST_DrawLevelField(x, y);
8227
8228         return;
8229       }
8230     }
8231     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8232     {
8233       if (IS_FOOD_PIG(Tile[newx][newy]))
8234       {
8235         if (IS_MOVING(newx, newy))
8236           RemoveMovingField(newx, newy);
8237         else
8238         {
8239           Tile[newx][newy] = EL_EMPTY;
8240           TEST_DrawLevelField(newx, newy);
8241         }
8242
8243         PlayLevelSound(x, y, SND_PIG_DIGGING);
8244       }
8245       else if (!IS_FREE(newx, newy))
8246       {
8247         if (IS_PLAYER(x, y))
8248           DrawPlayerField(x, y);
8249         else
8250           TEST_DrawLevelField(x, y);
8251
8252         return;
8253       }
8254     }
8255     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8256     {
8257       if (Store[x][y] != EL_EMPTY)
8258       {
8259         boolean can_clone = FALSE;
8260         int xx, yy;
8261
8262         // check if element to clone is still there
8263         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8264         {
8265           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8266           {
8267             can_clone = TRUE;
8268
8269             break;
8270           }
8271         }
8272
8273         // cannot clone or target field not free anymore -- do not clone
8274         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8275           Store[x][y] = EL_EMPTY;
8276       }
8277
8278       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8279       {
8280         if (IS_MV_DIAGONAL(MovDir[x][y]))
8281         {
8282           int diagonal_move_dir = MovDir[x][y];
8283           int stored = Store[x][y];
8284           int change_delay = 8;
8285           int graphic;
8286
8287           // android is moving diagonally
8288
8289           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8290
8291           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8292           GfxElement[x][y] = EL_EMC_ANDROID;
8293           GfxAction[x][y] = ACTION_SHRINKING;
8294           GfxDir[x][y] = diagonal_move_dir;
8295           ChangeDelay[x][y] = change_delay;
8296
8297           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8298                                    GfxDir[x][y]);
8299
8300           DrawLevelGraphicAnimation(x, y, graphic);
8301           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8302
8303           if (Tile[newx][newy] == EL_ACID)
8304           {
8305             SplashAcid(newx, newy);
8306
8307             return;
8308           }
8309
8310           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8311
8312           Store[newx][newy] = EL_EMC_ANDROID;
8313           GfxElement[newx][newy] = EL_EMC_ANDROID;
8314           GfxAction[newx][newy] = ACTION_GROWING;
8315           GfxDir[newx][newy] = diagonal_move_dir;
8316           ChangeDelay[newx][newy] = change_delay;
8317
8318           graphic = el_act_dir2img(GfxElement[newx][newy],
8319                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8320
8321           DrawLevelGraphicAnimation(newx, newy, graphic);
8322           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8323
8324           return;
8325         }
8326         else
8327         {
8328           Tile[newx][newy] = EL_EMPTY;
8329           TEST_DrawLevelField(newx, newy);
8330
8331           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8332         }
8333       }
8334       else if (!IS_FREE(newx, newy))
8335       {
8336         return;
8337       }
8338     }
8339     else if (IS_CUSTOM_ELEMENT(element) &&
8340              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8341     {
8342       if (!DigFieldByCE(newx, newy, element))
8343         return;
8344
8345       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8346       {
8347         RunnerVisit[x][y] = FrameCounter;
8348         PlayerVisit[x][y] /= 8;         // expire player visit path
8349       }
8350     }
8351     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8352     {
8353       if (!IS_FREE(newx, newy))
8354       {
8355         if (IS_PLAYER(x, y))
8356           DrawPlayerField(x, y);
8357         else
8358           TEST_DrawLevelField(x, y);
8359
8360         return;
8361       }
8362       else
8363       {
8364         boolean wanna_flame = !RND(10);
8365         int dx = newx - x, dy = newy - y;
8366         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8367         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8368         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8369                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8370         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8371                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8372
8373         if ((wanna_flame ||
8374              IS_CLASSIC_ENEMY(element1) ||
8375              IS_CLASSIC_ENEMY(element2)) &&
8376             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8377             element1 != EL_FLAMES && element2 != EL_FLAMES)
8378         {
8379           ResetGfxAnimation(x, y);
8380           GfxAction[x][y] = ACTION_ATTACKING;
8381
8382           if (IS_PLAYER(x, y))
8383             DrawPlayerField(x, y);
8384           else
8385             TEST_DrawLevelField(x, y);
8386
8387           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8388
8389           MovDelay[x][y] = 50;
8390
8391           Tile[newx][newy] = EL_FLAMES;
8392           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8393             Tile[newx1][newy1] = EL_FLAMES;
8394           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8395             Tile[newx2][newy2] = EL_FLAMES;
8396
8397           return;
8398         }
8399       }
8400     }
8401     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8402              Tile[newx][newy] == EL_DIAMOND)
8403     {
8404       if (IS_MOVING(newx, newy))
8405         RemoveMovingField(newx, newy);
8406       else
8407       {
8408         Tile[newx][newy] = EL_EMPTY;
8409         TEST_DrawLevelField(newx, newy);
8410       }
8411
8412       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8413     }
8414     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8415              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8416     {
8417       if (AmoebaNr[newx][newy])
8418       {
8419         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8420         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8421             Tile[newx][newy] == EL_BD_AMOEBA)
8422           AmoebaCnt[AmoebaNr[newx][newy]]--;
8423       }
8424
8425       if (IS_MOVING(newx, newy))
8426       {
8427         RemoveMovingField(newx, newy);
8428       }
8429       else
8430       {
8431         Tile[newx][newy] = EL_EMPTY;
8432         TEST_DrawLevelField(newx, newy);
8433       }
8434
8435       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8436     }
8437     else if ((element == EL_PACMAN || element == EL_MOLE)
8438              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8439     {
8440       if (AmoebaNr[newx][newy])
8441       {
8442         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8443         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8444             Tile[newx][newy] == EL_BD_AMOEBA)
8445           AmoebaCnt[AmoebaNr[newx][newy]]--;
8446       }
8447
8448       if (element == EL_MOLE)
8449       {
8450         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8451         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8452
8453         ResetGfxAnimation(x, y);
8454         GfxAction[x][y] = ACTION_DIGGING;
8455         TEST_DrawLevelField(x, y);
8456
8457         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8458
8459         return;                         // wait for shrinking amoeba
8460       }
8461       else      // element == EL_PACMAN
8462       {
8463         Tile[newx][newy] = EL_EMPTY;
8464         TEST_DrawLevelField(newx, newy);
8465         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8466       }
8467     }
8468     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8469              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8470               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8471     {
8472       // wait for shrinking amoeba to completely disappear
8473       return;
8474     }
8475     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8476     {
8477       // object was running against a wall
8478
8479       TurnRound(x, y);
8480
8481       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8482         DrawLevelElementAnimation(x, y, element);
8483
8484       if (DONT_TOUCH(element))
8485         TestIfBadThingTouchesPlayer(x, y);
8486
8487       return;
8488     }
8489
8490     InitMovingField(x, y, MovDir[x][y]);
8491
8492     PlayLevelSoundAction(x, y, ACTION_MOVING);
8493   }
8494
8495   if (MovDir[x][y])
8496     ContinueMoving(x, y);
8497 }
8498
8499 void ContinueMoving(int x, int y)
8500 {
8501   int element = Tile[x][y];
8502   struct ElementInfo *ei = &element_info[element];
8503   int direction = MovDir[x][y];
8504   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8505   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8506   int newx = x + dx, newy = y + dy;
8507   int stored = Store[x][y];
8508   int stored_new = Store[newx][newy];
8509   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8510   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8511   boolean last_line = (newy == lev_fieldy - 1);
8512
8513   MovPos[x][y] += getElementMoveStepsize(x, y);
8514
8515   if (pushed_by_player) // special case: moving object pushed by player
8516     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8517
8518   if (ABS(MovPos[x][y]) < TILEX)
8519   {
8520     TEST_DrawLevelField(x, y);
8521
8522     return;     // element is still moving
8523   }
8524
8525   // element reached destination field
8526
8527   Tile[x][y] = EL_EMPTY;
8528   Tile[newx][newy] = element;
8529   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8530
8531   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8532   {
8533     element = Tile[newx][newy] = EL_ACID;
8534   }
8535   else if (element == EL_MOLE)
8536   {
8537     Tile[x][y] = EL_SAND;
8538
8539     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8540   }
8541   else if (element == EL_QUICKSAND_FILLING)
8542   {
8543     element = Tile[newx][newy] = get_next_element(element);
8544     Store[newx][newy] = Store[x][y];
8545   }
8546   else if (element == EL_QUICKSAND_EMPTYING)
8547   {
8548     Tile[x][y] = get_next_element(element);
8549     element = Tile[newx][newy] = Store[x][y];
8550   }
8551   else if (element == EL_QUICKSAND_FAST_FILLING)
8552   {
8553     element = Tile[newx][newy] = get_next_element(element);
8554     Store[newx][newy] = Store[x][y];
8555   }
8556   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8557   {
8558     Tile[x][y] = get_next_element(element);
8559     element = Tile[newx][newy] = Store[x][y];
8560   }
8561   else if (element == EL_MAGIC_WALL_FILLING)
8562   {
8563     element = Tile[newx][newy] = get_next_element(element);
8564     if (!game.magic_wall_active)
8565       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8566     Store[newx][newy] = Store[x][y];
8567   }
8568   else if (element == EL_MAGIC_WALL_EMPTYING)
8569   {
8570     Tile[x][y] = get_next_element(element);
8571     if (!game.magic_wall_active)
8572       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8573     element = Tile[newx][newy] = Store[x][y];
8574
8575     InitField(newx, newy, FALSE);
8576   }
8577   else if (element == EL_BD_MAGIC_WALL_FILLING)
8578   {
8579     element = Tile[newx][newy] = get_next_element(element);
8580     if (!game.magic_wall_active)
8581       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8582     Store[newx][newy] = Store[x][y];
8583   }
8584   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8585   {
8586     Tile[x][y] = get_next_element(element);
8587     if (!game.magic_wall_active)
8588       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8589     element = Tile[newx][newy] = Store[x][y];
8590
8591     InitField(newx, newy, FALSE);
8592   }
8593   else if (element == EL_DC_MAGIC_WALL_FILLING)
8594   {
8595     element = Tile[newx][newy] = get_next_element(element);
8596     if (!game.magic_wall_active)
8597       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8598     Store[newx][newy] = Store[x][y];
8599   }
8600   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8601   {
8602     Tile[x][y] = get_next_element(element);
8603     if (!game.magic_wall_active)
8604       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8605     element = Tile[newx][newy] = Store[x][y];
8606
8607     InitField(newx, newy, FALSE);
8608   }
8609   else if (element == EL_AMOEBA_DROPPING)
8610   {
8611     Tile[x][y] = get_next_element(element);
8612     element = Tile[newx][newy] = Store[x][y];
8613   }
8614   else if (element == EL_SOKOBAN_OBJECT)
8615   {
8616     if (Back[x][y])
8617       Tile[x][y] = Back[x][y];
8618
8619     if (Back[newx][newy])
8620       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8621
8622     Back[x][y] = Back[newx][newy] = 0;
8623   }
8624
8625   Store[x][y] = EL_EMPTY;
8626   MovPos[x][y] = 0;
8627   MovDir[x][y] = 0;
8628   MovDelay[x][y] = 0;
8629
8630   MovDelay[newx][newy] = 0;
8631
8632   if (CAN_CHANGE_OR_HAS_ACTION(element))
8633   {
8634     // copy element change control values to new field
8635     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8636     ChangePage[newx][newy]  = ChangePage[x][y];
8637     ChangeCount[newx][newy] = ChangeCount[x][y];
8638     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8639   }
8640
8641   CustomValue[newx][newy] = CustomValue[x][y];
8642
8643   ChangeDelay[x][y] = 0;
8644   ChangePage[x][y] = -1;
8645   ChangeCount[x][y] = 0;
8646   ChangeEvent[x][y] = -1;
8647
8648   CustomValue[x][y] = 0;
8649
8650   // copy animation control values to new field
8651   GfxFrame[newx][newy]  = GfxFrame[x][y];
8652   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8653   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8654   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8655
8656   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8657
8658   // some elements can leave other elements behind after moving
8659   if (ei->move_leave_element != EL_EMPTY &&
8660       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8661       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8662   {
8663     int move_leave_element = ei->move_leave_element;
8664
8665     // this makes it possible to leave the removed element again
8666     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8667       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8668
8669     Tile[x][y] = move_leave_element;
8670
8671     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8672       MovDir[x][y] = direction;
8673
8674     InitField(x, y, FALSE);
8675
8676     if (GFX_CRUMBLED(Tile[x][y]))
8677       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8678
8679     if (ELEM_IS_PLAYER(move_leave_element))
8680       RelocatePlayer(x, y, move_leave_element);
8681   }
8682
8683   // do this after checking for left-behind element
8684   ResetGfxAnimation(x, y);      // reset animation values for old field
8685
8686   if (!CAN_MOVE(element) ||
8687       (CAN_FALL(element) && direction == MV_DOWN &&
8688        (element == EL_SPRING ||
8689         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8690         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8691     GfxDir[x][y] = MovDir[newx][newy] = 0;
8692
8693   TEST_DrawLevelField(x, y);
8694   TEST_DrawLevelField(newx, newy);
8695
8696   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8697
8698   // prevent pushed element from moving on in pushed direction
8699   if (pushed_by_player && CAN_MOVE(element) &&
8700       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8701       !(element_info[element].move_pattern & direction))
8702     TurnRound(newx, newy);
8703
8704   // prevent elements on conveyor belt from moving on in last direction
8705   if (pushed_by_conveyor && CAN_FALL(element) &&
8706       direction & MV_HORIZONTAL)
8707     MovDir[newx][newy] = 0;
8708
8709   if (!pushed_by_player)
8710   {
8711     int nextx = newx + dx, nexty = newy + dy;
8712     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8713
8714     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8715
8716     if (CAN_FALL(element) && direction == MV_DOWN)
8717       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8718
8719     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8720       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8721
8722     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8723       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8724   }
8725
8726   if (DONT_TOUCH(element))      // object may be nasty to player or others
8727   {
8728     TestIfBadThingTouchesPlayer(newx, newy);
8729     TestIfBadThingTouchesFriend(newx, newy);
8730
8731     if (!IS_CUSTOM_ELEMENT(element))
8732       TestIfBadThingTouchesOtherBadThing(newx, newy);
8733   }
8734   else if (element == EL_PENGUIN)
8735     TestIfFriendTouchesBadThing(newx, newy);
8736
8737   if (DONT_GET_HIT_BY(element))
8738   {
8739     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8740   }
8741
8742   // give the player one last chance (one more frame) to move away
8743   if (CAN_FALL(element) && direction == MV_DOWN &&
8744       (last_line || (!IS_FREE(x, newy + 1) &&
8745                      (!IS_PLAYER(x, newy + 1) ||
8746                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8747     Impact(x, newy);
8748
8749   if (pushed_by_player && !game.use_change_when_pushing_bug)
8750   {
8751     int push_side = MV_DIR_OPPOSITE(direction);
8752     struct PlayerInfo *player = PLAYERINFO(x, y);
8753
8754     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8755                                player->index_bit, push_side);
8756     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8757                                         player->index_bit, push_side);
8758   }
8759
8760   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8761     MovDelay[newx][newy] = 1;
8762
8763   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8764
8765   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8766   TestIfElementHitsCustomElement(newx, newy, direction);
8767   TestIfPlayerTouchesCustomElement(newx, newy);
8768   TestIfElementTouchesCustomElement(newx, newy);
8769
8770   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8771       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8772     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8773                              MV_DIR_OPPOSITE(direction));
8774 }
8775
8776 int AmoebaNeighbourNr(int ax, int ay)
8777 {
8778   int i;
8779   int element = Tile[ax][ay];
8780   int group_nr = 0;
8781   static int xy[4][2] =
8782   {
8783     { 0, -1 },
8784     { -1, 0 },
8785     { +1, 0 },
8786     { 0, +1 }
8787   };
8788
8789   for (i = 0; i < NUM_DIRECTIONS; i++)
8790   {
8791     int x = ax + xy[i][0];
8792     int y = ay + xy[i][1];
8793
8794     if (!IN_LEV_FIELD(x, y))
8795       continue;
8796
8797     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8798       group_nr = AmoebaNr[x][y];
8799   }
8800
8801   return group_nr;
8802 }
8803
8804 static void AmoebaMerge(int ax, int ay)
8805 {
8806   int i, x, y, xx, yy;
8807   int new_group_nr = AmoebaNr[ax][ay];
8808   static int xy[4][2] =
8809   {
8810     { 0, -1 },
8811     { -1, 0 },
8812     { +1, 0 },
8813     { 0, +1 }
8814   };
8815
8816   if (new_group_nr == 0)
8817     return;
8818
8819   for (i = 0; i < NUM_DIRECTIONS; i++)
8820   {
8821     x = ax + xy[i][0];
8822     y = ay + xy[i][1];
8823
8824     if (!IN_LEV_FIELD(x, y))
8825       continue;
8826
8827     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8828          Tile[x][y] == EL_BD_AMOEBA ||
8829          Tile[x][y] == EL_AMOEBA_DEAD) &&
8830         AmoebaNr[x][y] != new_group_nr)
8831     {
8832       int old_group_nr = AmoebaNr[x][y];
8833
8834       if (old_group_nr == 0)
8835         return;
8836
8837       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8838       AmoebaCnt[old_group_nr] = 0;
8839       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8840       AmoebaCnt2[old_group_nr] = 0;
8841
8842       SCAN_PLAYFIELD(xx, yy)
8843       {
8844         if (AmoebaNr[xx][yy] == old_group_nr)
8845           AmoebaNr[xx][yy] = new_group_nr;
8846       }
8847     }
8848   }
8849 }
8850
8851 void AmoebaToDiamond(int ax, int ay)
8852 {
8853   int i, x, y;
8854
8855   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8856   {
8857     int group_nr = AmoebaNr[ax][ay];
8858
8859 #ifdef DEBUG
8860     if (group_nr == 0)
8861     {
8862       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8863       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8864
8865       return;
8866     }
8867 #endif
8868
8869     SCAN_PLAYFIELD(x, y)
8870     {
8871       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8872       {
8873         AmoebaNr[x][y] = 0;
8874         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8875       }
8876     }
8877
8878     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8879                             SND_AMOEBA_TURNING_TO_GEM :
8880                             SND_AMOEBA_TURNING_TO_ROCK));
8881     Bang(ax, ay);
8882   }
8883   else
8884   {
8885     static int xy[4][2] =
8886     {
8887       { 0, -1 },
8888       { -1, 0 },
8889       { +1, 0 },
8890       { 0, +1 }
8891     };
8892
8893     for (i = 0; i < NUM_DIRECTIONS; i++)
8894     {
8895       x = ax + xy[i][0];
8896       y = ay + xy[i][1];
8897
8898       if (!IN_LEV_FIELD(x, y))
8899         continue;
8900
8901       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8902       {
8903         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8904                               SND_AMOEBA_TURNING_TO_GEM :
8905                               SND_AMOEBA_TURNING_TO_ROCK));
8906         Bang(x, y);
8907       }
8908     }
8909   }
8910 }
8911
8912 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8913 {
8914   int x, y;
8915   int group_nr = AmoebaNr[ax][ay];
8916   boolean done = FALSE;
8917
8918 #ifdef DEBUG
8919   if (group_nr == 0)
8920   {
8921     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8922     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8923
8924     return;
8925   }
8926 #endif
8927
8928   SCAN_PLAYFIELD(x, y)
8929   {
8930     if (AmoebaNr[x][y] == group_nr &&
8931         (Tile[x][y] == EL_AMOEBA_DEAD ||
8932          Tile[x][y] == EL_BD_AMOEBA ||
8933          Tile[x][y] == EL_AMOEBA_GROWING))
8934     {
8935       AmoebaNr[x][y] = 0;
8936       Tile[x][y] = new_element;
8937       InitField(x, y, FALSE);
8938       TEST_DrawLevelField(x, y);
8939       done = TRUE;
8940     }
8941   }
8942
8943   if (done)
8944     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8945                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8946                             SND_BD_AMOEBA_TURNING_TO_GEM));
8947 }
8948
8949 static void AmoebaGrowing(int x, int y)
8950 {
8951   static unsigned int sound_delay = 0;
8952   static unsigned int sound_delay_value = 0;
8953
8954   if (!MovDelay[x][y])          // start new growing cycle
8955   {
8956     MovDelay[x][y] = 7;
8957
8958     if (DelayReached(&sound_delay, sound_delay_value))
8959     {
8960       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8961       sound_delay_value = 30;
8962     }
8963   }
8964
8965   if (MovDelay[x][y])           // wait some time before growing bigger
8966   {
8967     MovDelay[x][y]--;
8968     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8969     {
8970       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8971                                            6 - MovDelay[x][y]);
8972
8973       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8974     }
8975
8976     if (!MovDelay[x][y])
8977     {
8978       Tile[x][y] = Store[x][y];
8979       Store[x][y] = 0;
8980       TEST_DrawLevelField(x, y);
8981     }
8982   }
8983 }
8984
8985 static void AmoebaShrinking(int x, int y)
8986 {
8987   static unsigned int sound_delay = 0;
8988   static unsigned int sound_delay_value = 0;
8989
8990   if (!MovDelay[x][y])          // start new shrinking cycle
8991   {
8992     MovDelay[x][y] = 7;
8993
8994     if (DelayReached(&sound_delay, sound_delay_value))
8995       sound_delay_value = 30;
8996   }
8997
8998   if (MovDelay[x][y])           // wait some time before shrinking
8999   {
9000     MovDelay[x][y]--;
9001     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9002     {
9003       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9004                                            6 - MovDelay[x][y]);
9005
9006       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9007     }
9008
9009     if (!MovDelay[x][y])
9010     {
9011       Tile[x][y] = EL_EMPTY;
9012       TEST_DrawLevelField(x, y);
9013
9014       // don't let mole enter this field in this cycle;
9015       // (give priority to objects falling to this field from above)
9016       Stop[x][y] = TRUE;
9017     }
9018   }
9019 }
9020
9021 static void AmoebaReproduce(int ax, int ay)
9022 {
9023   int i;
9024   int element = Tile[ax][ay];
9025   int graphic = el2img(element);
9026   int newax = ax, neway = ay;
9027   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9028   static int xy[4][2] =
9029   {
9030     { 0, -1 },
9031     { -1, 0 },
9032     { +1, 0 },
9033     { 0, +1 }
9034   };
9035
9036   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9037   {
9038     Tile[ax][ay] = EL_AMOEBA_DEAD;
9039     TEST_DrawLevelField(ax, ay);
9040     return;
9041   }
9042
9043   if (IS_ANIMATED(graphic))
9044     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9045
9046   if (!MovDelay[ax][ay])        // start making new amoeba field
9047     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9048
9049   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9050   {
9051     MovDelay[ax][ay]--;
9052     if (MovDelay[ax][ay])
9053       return;
9054   }
9055
9056   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9057   {
9058     int start = RND(4);
9059     int x = ax + xy[start][0];
9060     int y = ay + xy[start][1];
9061
9062     if (!IN_LEV_FIELD(x, y))
9063       return;
9064
9065     if (IS_FREE(x, y) ||
9066         CAN_GROW_INTO(Tile[x][y]) ||
9067         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9068         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9069     {
9070       newax = x;
9071       neway = y;
9072     }
9073
9074     if (newax == ax && neway == ay)
9075       return;
9076   }
9077   else                          // normal or "filled" (BD style) amoeba
9078   {
9079     int start = RND(4);
9080     boolean waiting_for_player = FALSE;
9081
9082     for (i = 0; i < NUM_DIRECTIONS; i++)
9083     {
9084       int j = (start + i) % 4;
9085       int x = ax + xy[j][0];
9086       int y = ay + xy[j][1];
9087
9088       if (!IN_LEV_FIELD(x, y))
9089         continue;
9090
9091       if (IS_FREE(x, y) ||
9092           CAN_GROW_INTO(Tile[x][y]) ||
9093           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9094           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9095       {
9096         newax = x;
9097         neway = y;
9098         break;
9099       }
9100       else if (IS_PLAYER(x, y))
9101         waiting_for_player = TRUE;
9102     }
9103
9104     if (newax == ax && neway == ay)             // amoeba cannot grow
9105     {
9106       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9107       {
9108         Tile[ax][ay] = EL_AMOEBA_DEAD;
9109         TEST_DrawLevelField(ax, ay);
9110         AmoebaCnt[AmoebaNr[ax][ay]]--;
9111
9112         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9113         {
9114           if (element == EL_AMOEBA_FULL)
9115             AmoebaToDiamond(ax, ay);
9116           else if (element == EL_BD_AMOEBA)
9117             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9118         }
9119       }
9120       return;
9121     }
9122     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9123     {
9124       // amoeba gets larger by growing in some direction
9125
9126       int new_group_nr = AmoebaNr[ax][ay];
9127
9128 #ifdef DEBUG
9129   if (new_group_nr == 0)
9130   {
9131     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9132           newax, neway);
9133     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9134
9135     return;
9136   }
9137 #endif
9138
9139       AmoebaNr[newax][neway] = new_group_nr;
9140       AmoebaCnt[new_group_nr]++;
9141       AmoebaCnt2[new_group_nr]++;
9142
9143       // if amoeba touches other amoeba(s) after growing, unify them
9144       AmoebaMerge(newax, neway);
9145
9146       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9147       {
9148         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9149         return;
9150       }
9151     }
9152   }
9153
9154   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9155       (neway == lev_fieldy - 1 && newax != ax))
9156   {
9157     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9158     Store[newax][neway] = element;
9159   }
9160   else if (neway == ay || element == EL_EMC_DRIPPER)
9161   {
9162     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9163
9164     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9165   }
9166   else
9167   {
9168     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9169     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9170     Store[ax][ay] = EL_AMOEBA_DROP;
9171     ContinueMoving(ax, ay);
9172     return;
9173   }
9174
9175   TEST_DrawLevelField(newax, neway);
9176 }
9177
9178 static void Life(int ax, int ay)
9179 {
9180   int x1, y1, x2, y2;
9181   int life_time = 40;
9182   int element = Tile[ax][ay];
9183   int graphic = el2img(element);
9184   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9185                          level.biomaze);
9186   boolean changed = FALSE;
9187
9188   if (IS_ANIMATED(graphic))
9189     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9190
9191   if (Stop[ax][ay])
9192     return;
9193
9194   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9195     MovDelay[ax][ay] = life_time;
9196
9197   if (MovDelay[ax][ay])         // wait some time before next cycle
9198   {
9199     MovDelay[ax][ay]--;
9200     if (MovDelay[ax][ay])
9201       return;
9202   }
9203
9204   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9205   {
9206     int xx = ax+x1, yy = ay+y1;
9207     int old_element = Tile[xx][yy];
9208     int num_neighbours = 0;
9209
9210     if (!IN_LEV_FIELD(xx, yy))
9211       continue;
9212
9213     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9214     {
9215       int x = xx+x2, y = yy+y2;
9216
9217       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9218         continue;
9219
9220       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9221       boolean is_neighbour = FALSE;
9222
9223       if (level.use_life_bugs)
9224         is_neighbour =
9225           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9226            (IS_FREE(x, y)                             &&  Stop[x][y]));
9227       else
9228         is_neighbour =
9229           (Last[x][y] == element || is_player_cell);
9230
9231       if (is_neighbour)
9232         num_neighbours++;
9233     }
9234
9235     boolean is_free = FALSE;
9236
9237     if (level.use_life_bugs)
9238       is_free = (IS_FREE(xx, yy));
9239     else
9240       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9241
9242     if (xx == ax && yy == ay)           // field in the middle
9243     {
9244       if (num_neighbours < life_parameter[0] ||
9245           num_neighbours > life_parameter[1])
9246       {
9247         Tile[xx][yy] = EL_EMPTY;
9248         if (Tile[xx][yy] != old_element)
9249           TEST_DrawLevelField(xx, yy);
9250         Stop[xx][yy] = TRUE;
9251         changed = TRUE;
9252       }
9253     }
9254     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9255     {                                   // free border field
9256       if (num_neighbours >= life_parameter[2] &&
9257           num_neighbours <= life_parameter[3])
9258       {
9259         Tile[xx][yy] = element;
9260         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9261         if (Tile[xx][yy] != old_element)
9262           TEST_DrawLevelField(xx, yy);
9263         Stop[xx][yy] = TRUE;
9264         changed = TRUE;
9265       }
9266     }
9267   }
9268
9269   if (changed)
9270     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9271                    SND_GAME_OF_LIFE_GROWING);
9272 }
9273
9274 static void InitRobotWheel(int x, int y)
9275 {
9276   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9277 }
9278
9279 static void RunRobotWheel(int x, int y)
9280 {
9281   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9282 }
9283
9284 static void StopRobotWheel(int x, int y)
9285 {
9286   if (game.robot_wheel_x == x &&
9287       game.robot_wheel_y == y)
9288   {
9289     game.robot_wheel_x = -1;
9290     game.robot_wheel_y = -1;
9291     game.robot_wheel_active = FALSE;
9292   }
9293 }
9294
9295 static void InitTimegateWheel(int x, int y)
9296 {
9297   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9298 }
9299
9300 static void RunTimegateWheel(int x, int y)
9301 {
9302   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9303 }
9304
9305 static void InitMagicBallDelay(int x, int y)
9306 {
9307   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9308 }
9309
9310 static void ActivateMagicBall(int bx, int by)
9311 {
9312   int x, y;
9313
9314   if (level.ball_random)
9315   {
9316     int pos_border = RND(8);    // select one of the eight border elements
9317     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9318     int xx = pos_content % 3;
9319     int yy = pos_content / 3;
9320
9321     x = bx - 1 + xx;
9322     y = by - 1 + yy;
9323
9324     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9325       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9326   }
9327   else
9328   {
9329     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9330     {
9331       int xx = x - bx + 1;
9332       int yy = y - by + 1;
9333
9334       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9335         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9336     }
9337   }
9338
9339   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9340 }
9341
9342 static void CheckExit(int x, int y)
9343 {
9344   if (game.gems_still_needed > 0 ||
9345       game.sokoban_fields_still_needed > 0 ||
9346       game.sokoban_objects_still_needed > 0 ||
9347       game.lights_still_needed > 0)
9348   {
9349     int element = Tile[x][y];
9350     int graphic = el2img(element);
9351
9352     if (IS_ANIMATED(graphic))
9353       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9354
9355     return;
9356   }
9357
9358   // do not re-open exit door closed after last player
9359   if (game.all_players_gone)
9360     return;
9361
9362   Tile[x][y] = EL_EXIT_OPENING;
9363
9364   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9365 }
9366
9367 static void CheckExitEM(int x, int y)
9368 {
9369   if (game.gems_still_needed > 0 ||
9370       game.sokoban_fields_still_needed > 0 ||
9371       game.sokoban_objects_still_needed > 0 ||
9372       game.lights_still_needed > 0)
9373   {
9374     int element = Tile[x][y];
9375     int graphic = el2img(element);
9376
9377     if (IS_ANIMATED(graphic))
9378       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9379
9380     return;
9381   }
9382
9383   // do not re-open exit door closed after last player
9384   if (game.all_players_gone)
9385     return;
9386
9387   Tile[x][y] = EL_EM_EXIT_OPENING;
9388
9389   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9390 }
9391
9392 static void CheckExitSteel(int x, int y)
9393 {
9394   if (game.gems_still_needed > 0 ||
9395       game.sokoban_fields_still_needed > 0 ||
9396       game.sokoban_objects_still_needed > 0 ||
9397       game.lights_still_needed > 0)
9398   {
9399     int element = Tile[x][y];
9400     int graphic = el2img(element);
9401
9402     if (IS_ANIMATED(graphic))
9403       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9404
9405     return;
9406   }
9407
9408   // do not re-open exit door closed after last player
9409   if (game.all_players_gone)
9410     return;
9411
9412   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9413
9414   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9415 }
9416
9417 static void CheckExitSteelEM(int x, int y)
9418 {
9419   if (game.gems_still_needed > 0 ||
9420       game.sokoban_fields_still_needed > 0 ||
9421       game.sokoban_objects_still_needed > 0 ||
9422       game.lights_still_needed > 0)
9423   {
9424     int element = Tile[x][y];
9425     int graphic = el2img(element);
9426
9427     if (IS_ANIMATED(graphic))
9428       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9429
9430     return;
9431   }
9432
9433   // do not re-open exit door closed after last player
9434   if (game.all_players_gone)
9435     return;
9436
9437   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9438
9439   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9440 }
9441
9442 static void CheckExitSP(int x, int y)
9443 {
9444   if (game.gems_still_needed > 0)
9445   {
9446     int element = Tile[x][y];
9447     int graphic = el2img(element);
9448
9449     if (IS_ANIMATED(graphic))
9450       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9451
9452     return;
9453   }
9454
9455   // do not re-open exit door closed after last player
9456   if (game.all_players_gone)
9457     return;
9458
9459   Tile[x][y] = EL_SP_EXIT_OPENING;
9460
9461   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9462 }
9463
9464 static void CloseAllOpenTimegates(void)
9465 {
9466   int x, y;
9467
9468   SCAN_PLAYFIELD(x, y)
9469   {
9470     int element = Tile[x][y];
9471
9472     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9473     {
9474       Tile[x][y] = EL_TIMEGATE_CLOSING;
9475
9476       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9477     }
9478   }
9479 }
9480
9481 static void DrawTwinkleOnField(int x, int y)
9482 {
9483   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9484     return;
9485
9486   if (Tile[x][y] == EL_BD_DIAMOND)
9487     return;
9488
9489   if (MovDelay[x][y] == 0)      // next animation frame
9490     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9491
9492   if (MovDelay[x][y] != 0)      // wait some time before next frame
9493   {
9494     MovDelay[x][y]--;
9495
9496     DrawLevelElementAnimation(x, y, Tile[x][y]);
9497
9498     if (MovDelay[x][y] != 0)
9499     {
9500       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9501                                            10 - MovDelay[x][y]);
9502
9503       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9504     }
9505   }
9506 }
9507
9508 static void MauerWaechst(int x, int y)
9509 {
9510   int delay = 6;
9511
9512   if (!MovDelay[x][y])          // next animation frame
9513     MovDelay[x][y] = 3 * delay;
9514
9515   if (MovDelay[x][y])           // wait some time before next frame
9516   {
9517     MovDelay[x][y]--;
9518
9519     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9520     {
9521       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9522       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9523
9524       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9525     }
9526
9527     if (!MovDelay[x][y])
9528     {
9529       if (MovDir[x][y] == MV_LEFT)
9530       {
9531         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9532           TEST_DrawLevelField(x - 1, y);
9533       }
9534       else if (MovDir[x][y] == MV_RIGHT)
9535       {
9536         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9537           TEST_DrawLevelField(x + 1, y);
9538       }
9539       else if (MovDir[x][y] == MV_UP)
9540       {
9541         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9542           TEST_DrawLevelField(x, y - 1);
9543       }
9544       else
9545       {
9546         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9547           TEST_DrawLevelField(x, y + 1);
9548       }
9549
9550       Tile[x][y] = Store[x][y];
9551       Store[x][y] = 0;
9552       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9553       TEST_DrawLevelField(x, y);
9554     }
9555   }
9556 }
9557
9558 static void MauerAbleger(int ax, int ay)
9559 {
9560   int element = Tile[ax][ay];
9561   int graphic = el2img(element);
9562   boolean oben_frei = FALSE, unten_frei = FALSE;
9563   boolean links_frei = FALSE, rechts_frei = FALSE;
9564   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9565   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9566   boolean new_wall = FALSE;
9567
9568   if (IS_ANIMATED(graphic))
9569     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9570
9571   if (!MovDelay[ax][ay])        // start building new wall
9572     MovDelay[ax][ay] = 6;
9573
9574   if (MovDelay[ax][ay])         // wait some time before building new wall
9575   {
9576     MovDelay[ax][ay]--;
9577     if (MovDelay[ax][ay])
9578       return;
9579   }
9580
9581   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9582     oben_frei = TRUE;
9583   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9584     unten_frei = TRUE;
9585   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9586     links_frei = TRUE;
9587   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9588     rechts_frei = TRUE;
9589
9590   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9591       element == EL_EXPANDABLE_WALL_ANY)
9592   {
9593     if (oben_frei)
9594     {
9595       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9596       Store[ax][ay-1] = element;
9597       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9598       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9599         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9600                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9601       new_wall = TRUE;
9602     }
9603     if (unten_frei)
9604     {
9605       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9606       Store[ax][ay+1] = element;
9607       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9608       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9609         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9610                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9611       new_wall = TRUE;
9612     }
9613   }
9614
9615   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9616       element == EL_EXPANDABLE_WALL_ANY ||
9617       element == EL_EXPANDABLE_WALL ||
9618       element == EL_BD_EXPANDABLE_WALL)
9619   {
9620     if (links_frei)
9621     {
9622       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9623       Store[ax-1][ay] = element;
9624       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9625       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9626         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9627                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9628       new_wall = TRUE;
9629     }
9630
9631     if (rechts_frei)
9632     {
9633       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9634       Store[ax+1][ay] = element;
9635       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9636       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9637         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9638                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9639       new_wall = TRUE;
9640     }
9641   }
9642
9643   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9644     TEST_DrawLevelField(ax, ay);
9645
9646   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9647     oben_massiv = TRUE;
9648   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9649     unten_massiv = TRUE;
9650   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9651     links_massiv = TRUE;
9652   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9653     rechts_massiv = TRUE;
9654
9655   if (((oben_massiv && unten_massiv) ||
9656        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9657        element == EL_EXPANDABLE_WALL) &&
9658       ((links_massiv && rechts_massiv) ||
9659        element == EL_EXPANDABLE_WALL_VERTICAL))
9660     Tile[ax][ay] = EL_WALL;
9661
9662   if (new_wall)
9663     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9664 }
9665
9666 static void MauerAblegerStahl(int ax, int ay)
9667 {
9668   int element = Tile[ax][ay];
9669   int graphic = el2img(element);
9670   boolean oben_frei = FALSE, unten_frei = FALSE;
9671   boolean links_frei = FALSE, rechts_frei = FALSE;
9672   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9673   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9674   boolean new_wall = FALSE;
9675
9676   if (IS_ANIMATED(graphic))
9677     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9678
9679   if (!MovDelay[ax][ay])        // start building new wall
9680     MovDelay[ax][ay] = 6;
9681
9682   if (MovDelay[ax][ay])         // wait some time before building new wall
9683   {
9684     MovDelay[ax][ay]--;
9685     if (MovDelay[ax][ay])
9686       return;
9687   }
9688
9689   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9690     oben_frei = TRUE;
9691   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9692     unten_frei = TRUE;
9693   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9694     links_frei = TRUE;
9695   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9696     rechts_frei = TRUE;
9697
9698   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9699       element == EL_EXPANDABLE_STEELWALL_ANY)
9700   {
9701     if (oben_frei)
9702     {
9703       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9704       Store[ax][ay-1] = element;
9705       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9706       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9707         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9708                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9709       new_wall = TRUE;
9710     }
9711     if (unten_frei)
9712     {
9713       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9714       Store[ax][ay+1] = element;
9715       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9716       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9717         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9718                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9719       new_wall = TRUE;
9720     }
9721   }
9722
9723   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9724       element == EL_EXPANDABLE_STEELWALL_ANY)
9725   {
9726     if (links_frei)
9727     {
9728       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9729       Store[ax-1][ay] = element;
9730       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9731       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9732         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9733                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9734       new_wall = TRUE;
9735     }
9736
9737     if (rechts_frei)
9738     {
9739       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9740       Store[ax+1][ay] = element;
9741       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9742       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9743         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9744                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9745       new_wall = TRUE;
9746     }
9747   }
9748
9749   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9750     oben_massiv = TRUE;
9751   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9752     unten_massiv = TRUE;
9753   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9754     links_massiv = TRUE;
9755   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9756     rechts_massiv = TRUE;
9757
9758   if (((oben_massiv && unten_massiv) ||
9759        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9760       ((links_massiv && rechts_massiv) ||
9761        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9762     Tile[ax][ay] = EL_STEELWALL;
9763
9764   if (new_wall)
9765     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9766 }
9767
9768 static void CheckForDragon(int x, int y)
9769 {
9770   int i, j;
9771   boolean dragon_found = FALSE;
9772   static int xy[4][2] =
9773   {
9774     { 0, -1 },
9775     { -1, 0 },
9776     { +1, 0 },
9777     { 0, +1 }
9778   };
9779
9780   for (i = 0; i < NUM_DIRECTIONS; i++)
9781   {
9782     for (j = 0; j < 4; j++)
9783     {
9784       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9785
9786       if (IN_LEV_FIELD(xx, yy) &&
9787           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9788       {
9789         if (Tile[xx][yy] == EL_DRAGON)
9790           dragon_found = TRUE;
9791       }
9792       else
9793         break;
9794     }
9795   }
9796
9797   if (!dragon_found)
9798   {
9799     for (i = 0; i < NUM_DIRECTIONS; i++)
9800     {
9801       for (j = 0; j < 3; j++)
9802       {
9803         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9804   
9805         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9806         {
9807           Tile[xx][yy] = EL_EMPTY;
9808           TEST_DrawLevelField(xx, yy);
9809         }
9810         else
9811           break;
9812       }
9813     }
9814   }
9815 }
9816
9817 static void InitBuggyBase(int x, int y)
9818 {
9819   int element = Tile[x][y];
9820   int activating_delay = FRAMES_PER_SECOND / 4;
9821
9822   ChangeDelay[x][y] =
9823     (element == EL_SP_BUGGY_BASE ?
9824      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9825      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9826      activating_delay :
9827      element == EL_SP_BUGGY_BASE_ACTIVE ?
9828      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9829 }
9830
9831 static void WarnBuggyBase(int x, int y)
9832 {
9833   int i;
9834   static int xy[4][2] =
9835   {
9836     { 0, -1 },
9837     { -1, 0 },
9838     { +1, 0 },
9839     { 0, +1 }
9840   };
9841
9842   for (i = 0; i < NUM_DIRECTIONS; i++)
9843   {
9844     int xx = x + xy[i][0];
9845     int yy = y + xy[i][1];
9846
9847     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9848     {
9849       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9850
9851       break;
9852     }
9853   }
9854 }
9855
9856 static void InitTrap(int x, int y)
9857 {
9858   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9859 }
9860
9861 static void ActivateTrap(int x, int y)
9862 {
9863   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9864 }
9865
9866 static void ChangeActiveTrap(int x, int y)
9867 {
9868   int graphic = IMG_TRAP_ACTIVE;
9869
9870   // if new animation frame was drawn, correct crumbled sand border
9871   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9872     TEST_DrawLevelFieldCrumbled(x, y);
9873 }
9874
9875 static int getSpecialActionElement(int element, int number, int base_element)
9876 {
9877   return (element != EL_EMPTY ? element :
9878           number != -1 ? base_element + number - 1 :
9879           EL_EMPTY);
9880 }
9881
9882 static int getModifiedActionNumber(int value_old, int operator, int operand,
9883                                    int value_min, int value_max)
9884 {
9885   int value_new = (operator == CA_MODE_SET      ? operand :
9886                    operator == CA_MODE_ADD      ? value_old + operand :
9887                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9888                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9889                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9890                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9891                    value_old);
9892
9893   return (value_new < value_min ? value_min :
9894           value_new > value_max ? value_max :
9895           value_new);
9896 }
9897
9898 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9899 {
9900   struct ElementInfo *ei = &element_info[element];
9901   struct ElementChangeInfo *change = &ei->change_page[page];
9902   int target_element = change->target_element;
9903   int action_type = change->action_type;
9904   int action_mode = change->action_mode;
9905   int action_arg = change->action_arg;
9906   int action_element = change->action_element;
9907   int i;
9908
9909   if (!change->has_action)
9910     return;
9911
9912   // ---------- determine action paramater values -----------------------------
9913
9914   int level_time_value =
9915     (level.time > 0 ? TimeLeft :
9916      TimePlayed);
9917
9918   int action_arg_element_raw =
9919     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9920      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9921      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9922      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9923      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9924      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9925      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9926      EL_EMPTY);
9927   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9928
9929   int action_arg_direction =
9930     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9931      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9932      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9933      change->actual_trigger_side :
9934      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9935      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9936      MV_NONE);
9937
9938   int action_arg_number_min =
9939     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9940      CA_ARG_MIN);
9941
9942   int action_arg_number_max =
9943     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9944      action_type == CA_SET_LEVEL_GEMS ? 999 :
9945      action_type == CA_SET_LEVEL_TIME ? 9999 :
9946      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9947      action_type == CA_SET_CE_VALUE ? 9999 :
9948      action_type == CA_SET_CE_SCORE ? 9999 :
9949      CA_ARG_MAX);
9950
9951   int action_arg_number_reset =
9952     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9953      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9954      action_type == CA_SET_LEVEL_TIME ? level.time :
9955      action_type == CA_SET_LEVEL_SCORE ? 0 :
9956      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9957      action_type == CA_SET_CE_SCORE ? 0 :
9958      0);
9959
9960   int action_arg_number =
9961     (action_arg <= CA_ARG_MAX ? action_arg :
9962      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9963      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9964      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9965      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9966      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9967      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9968      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9969      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9970      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9971      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9972      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9973      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9974      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9975      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9976      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9977      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9978      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9979      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9980      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9981      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9982      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9983      -1);
9984
9985   int action_arg_number_old =
9986     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9987      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9988      action_type == CA_SET_LEVEL_SCORE ? game.score :
9989      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9990      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9991      0);
9992
9993   int action_arg_number_new =
9994     getModifiedActionNumber(action_arg_number_old,
9995                             action_mode, action_arg_number,
9996                             action_arg_number_min, action_arg_number_max);
9997
9998   int trigger_player_bits =
9999     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10000      change->actual_trigger_player_bits : change->trigger_player);
10001
10002   int action_arg_player_bits =
10003     (action_arg >= CA_ARG_PLAYER_1 &&
10004      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10005      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10006      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10007      PLAYER_BITS_ANY);
10008
10009   // ---------- execute action  -----------------------------------------------
10010
10011   switch (action_type)
10012   {
10013     case CA_NO_ACTION:
10014     {
10015       return;
10016     }
10017
10018     // ---------- level actions  ----------------------------------------------
10019
10020     case CA_RESTART_LEVEL:
10021     {
10022       game.restart_level = TRUE;
10023
10024       break;
10025     }
10026
10027     case CA_SHOW_ENVELOPE:
10028     {
10029       int element = getSpecialActionElement(action_arg_element,
10030                                             action_arg_number, EL_ENVELOPE_1);
10031
10032       if (IS_ENVELOPE(element))
10033         local_player->show_envelope = element;
10034
10035       break;
10036     }
10037
10038     case CA_SET_LEVEL_TIME:
10039     {
10040       if (level.time > 0)       // only modify limited time value
10041       {
10042         TimeLeft = action_arg_number_new;
10043
10044         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10045
10046         DisplayGameControlValues();
10047
10048         if (!TimeLeft && setup.time_limit)
10049           for (i = 0; i < MAX_PLAYERS; i++)
10050             KillPlayer(&stored_player[i]);
10051       }
10052
10053       break;
10054     }
10055
10056     case CA_SET_LEVEL_SCORE:
10057     {
10058       game.score = action_arg_number_new;
10059
10060       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10061
10062       DisplayGameControlValues();
10063
10064       break;
10065     }
10066
10067     case CA_SET_LEVEL_GEMS:
10068     {
10069       game.gems_still_needed = action_arg_number_new;
10070
10071       game.snapshot.collected_item = TRUE;
10072
10073       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10074
10075       DisplayGameControlValues();
10076
10077       break;
10078     }
10079
10080     case CA_SET_LEVEL_WIND:
10081     {
10082       game.wind_direction = action_arg_direction;
10083
10084       break;
10085     }
10086
10087     case CA_SET_LEVEL_RANDOM_SEED:
10088     {
10089       // ensure that setting a new random seed while playing is predictable
10090       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10091
10092       break;
10093     }
10094
10095     // ---------- player actions  ---------------------------------------------
10096
10097     case CA_MOVE_PLAYER:
10098     case CA_MOVE_PLAYER_NEW:
10099     {
10100       // automatically move to the next field in specified direction
10101       for (i = 0; i < MAX_PLAYERS; i++)
10102         if (trigger_player_bits & (1 << i))
10103           if (action_type == CA_MOVE_PLAYER ||
10104               stored_player[i].MovPos == 0)
10105             stored_player[i].programmed_action = action_arg_direction;
10106
10107       break;
10108     }
10109
10110     case CA_EXIT_PLAYER:
10111     {
10112       for (i = 0; i < MAX_PLAYERS; i++)
10113         if (action_arg_player_bits & (1 << i))
10114           ExitPlayer(&stored_player[i]);
10115
10116       if (game.players_still_needed == 0)
10117         LevelSolved();
10118
10119       break;
10120     }
10121
10122     case CA_KILL_PLAYER:
10123     {
10124       for (i = 0; i < MAX_PLAYERS; i++)
10125         if (action_arg_player_bits & (1 << i))
10126           KillPlayer(&stored_player[i]);
10127
10128       break;
10129     }
10130
10131     case CA_SET_PLAYER_KEYS:
10132     {
10133       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10134       int element = getSpecialActionElement(action_arg_element,
10135                                             action_arg_number, EL_KEY_1);
10136
10137       if (IS_KEY(element))
10138       {
10139         for (i = 0; i < MAX_PLAYERS; i++)
10140         {
10141           if (trigger_player_bits & (1 << i))
10142           {
10143             stored_player[i].key[KEY_NR(element)] = key_state;
10144
10145             DrawGameDoorValues();
10146           }
10147         }
10148       }
10149
10150       break;
10151     }
10152
10153     case CA_SET_PLAYER_SPEED:
10154     {
10155       for (i = 0; i < MAX_PLAYERS; i++)
10156       {
10157         if (trigger_player_bits & (1 << i))
10158         {
10159           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10160
10161           if (action_arg == CA_ARG_SPEED_FASTER &&
10162               stored_player[i].cannot_move)
10163           {
10164             action_arg_number = STEPSIZE_VERY_SLOW;
10165           }
10166           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10167                    action_arg == CA_ARG_SPEED_FASTER)
10168           {
10169             action_arg_number = 2;
10170             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10171                            CA_MODE_MULTIPLY);
10172           }
10173           else if (action_arg == CA_ARG_NUMBER_RESET)
10174           {
10175             action_arg_number = level.initial_player_stepsize[i];
10176           }
10177
10178           move_stepsize =
10179             getModifiedActionNumber(move_stepsize,
10180                                     action_mode,
10181                                     action_arg_number,
10182                                     action_arg_number_min,
10183                                     action_arg_number_max);
10184
10185           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10186         }
10187       }
10188
10189       break;
10190     }
10191
10192     case CA_SET_PLAYER_SHIELD:
10193     {
10194       for (i = 0; i < MAX_PLAYERS; i++)
10195       {
10196         if (trigger_player_bits & (1 << i))
10197         {
10198           if (action_arg == CA_ARG_SHIELD_OFF)
10199           {
10200             stored_player[i].shield_normal_time_left = 0;
10201             stored_player[i].shield_deadly_time_left = 0;
10202           }
10203           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10204           {
10205             stored_player[i].shield_normal_time_left = 999999;
10206           }
10207           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10208           {
10209             stored_player[i].shield_normal_time_left = 999999;
10210             stored_player[i].shield_deadly_time_left = 999999;
10211           }
10212         }
10213       }
10214
10215       break;
10216     }
10217
10218     case CA_SET_PLAYER_GRAVITY:
10219     {
10220       for (i = 0; i < MAX_PLAYERS; i++)
10221       {
10222         if (trigger_player_bits & (1 << i))
10223         {
10224           stored_player[i].gravity =
10225             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10226              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10227              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10228              stored_player[i].gravity);
10229         }
10230       }
10231
10232       break;
10233     }
10234
10235     case CA_SET_PLAYER_ARTWORK:
10236     {
10237       for (i = 0; i < MAX_PLAYERS; i++)
10238       {
10239         if (trigger_player_bits & (1 << i))
10240         {
10241           int artwork_element = action_arg_element;
10242
10243           if (action_arg == CA_ARG_ELEMENT_RESET)
10244             artwork_element =
10245               (level.use_artwork_element[i] ? level.artwork_element[i] :
10246                stored_player[i].element_nr);
10247
10248           if (stored_player[i].artwork_element != artwork_element)
10249             stored_player[i].Frame = 0;
10250
10251           stored_player[i].artwork_element = artwork_element;
10252
10253           SetPlayerWaiting(&stored_player[i], FALSE);
10254
10255           // set number of special actions for bored and sleeping animation
10256           stored_player[i].num_special_action_bored =
10257             get_num_special_action(artwork_element,
10258                                    ACTION_BORING_1, ACTION_BORING_LAST);
10259           stored_player[i].num_special_action_sleeping =
10260             get_num_special_action(artwork_element,
10261                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10262         }
10263       }
10264
10265       break;
10266     }
10267
10268     case CA_SET_PLAYER_INVENTORY:
10269     {
10270       for (i = 0; i < MAX_PLAYERS; i++)
10271       {
10272         struct PlayerInfo *player = &stored_player[i];
10273         int j, k;
10274
10275         if (trigger_player_bits & (1 << i))
10276         {
10277           int inventory_element = action_arg_element;
10278
10279           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10280               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10281               action_arg == CA_ARG_ELEMENT_ACTION)
10282           {
10283             int element = inventory_element;
10284             int collect_count = element_info[element].collect_count_initial;
10285
10286             if (!IS_CUSTOM_ELEMENT(element))
10287               collect_count = 1;
10288
10289             if (collect_count == 0)
10290               player->inventory_infinite_element = element;
10291             else
10292               for (k = 0; k < collect_count; k++)
10293                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10294                   player->inventory_element[player->inventory_size++] =
10295                     element;
10296           }
10297           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10298                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10299                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10300           {
10301             if (player->inventory_infinite_element != EL_UNDEFINED &&
10302                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10303                                      action_arg_element_raw))
10304               player->inventory_infinite_element = EL_UNDEFINED;
10305
10306             for (k = 0, j = 0; j < player->inventory_size; j++)
10307             {
10308               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10309                                         action_arg_element_raw))
10310                 player->inventory_element[k++] = player->inventory_element[j];
10311             }
10312
10313             player->inventory_size = k;
10314           }
10315           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10316           {
10317             if (player->inventory_size > 0)
10318             {
10319               for (j = 0; j < player->inventory_size - 1; j++)
10320                 player->inventory_element[j] = player->inventory_element[j + 1];
10321
10322               player->inventory_size--;
10323             }
10324           }
10325           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10326           {
10327             if (player->inventory_size > 0)
10328               player->inventory_size--;
10329           }
10330           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10331           {
10332             player->inventory_infinite_element = EL_UNDEFINED;
10333             player->inventory_size = 0;
10334           }
10335           else if (action_arg == CA_ARG_INVENTORY_RESET)
10336           {
10337             player->inventory_infinite_element = EL_UNDEFINED;
10338             player->inventory_size = 0;
10339
10340             if (level.use_initial_inventory[i])
10341             {
10342               for (j = 0; j < level.initial_inventory_size[i]; j++)
10343               {
10344                 int element = level.initial_inventory_content[i][j];
10345                 int collect_count = element_info[element].collect_count_initial;
10346
10347                 if (!IS_CUSTOM_ELEMENT(element))
10348                   collect_count = 1;
10349
10350                 if (collect_count == 0)
10351                   player->inventory_infinite_element = element;
10352                 else
10353                   for (k = 0; k < collect_count; k++)
10354                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10355                       player->inventory_element[player->inventory_size++] =
10356                         element;
10357               }
10358             }
10359           }
10360         }
10361       }
10362
10363       break;
10364     }
10365
10366     // ---------- CE actions  -------------------------------------------------
10367
10368     case CA_SET_CE_VALUE:
10369     {
10370       int last_ce_value = CustomValue[x][y];
10371
10372       CustomValue[x][y] = action_arg_number_new;
10373
10374       if (CustomValue[x][y] != last_ce_value)
10375       {
10376         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10377         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10378
10379         if (CustomValue[x][y] == 0)
10380         {
10381           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10382           ChangeCount[x][y] = 0;        // allow at least one more change
10383
10384           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10385           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10386         }
10387       }
10388
10389       break;
10390     }
10391
10392     case CA_SET_CE_SCORE:
10393     {
10394       int last_ce_score = ei->collect_score;
10395
10396       ei->collect_score = action_arg_number_new;
10397
10398       if (ei->collect_score != last_ce_score)
10399       {
10400         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10401         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10402
10403         if (ei->collect_score == 0)
10404         {
10405           int xx, yy;
10406
10407           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10408           ChangeCount[x][y] = 0;        // allow at least one more change
10409
10410           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10411           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10412
10413           /*
10414             This is a very special case that seems to be a mixture between
10415             CheckElementChange() and CheckTriggeredElementChange(): while
10416             the first one only affects single elements that are triggered
10417             directly, the second one affects multiple elements in the playfield
10418             that are triggered indirectly by another element. This is a third
10419             case: Changing the CE score always affects multiple identical CEs,
10420             so every affected CE must be checked, not only the single CE for
10421             which the CE score was changed in the first place (as every instance
10422             of that CE shares the same CE score, and therefore also can change)!
10423           */
10424           SCAN_PLAYFIELD(xx, yy)
10425           {
10426             if (Tile[xx][yy] == element)
10427               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10428                                  CE_SCORE_GETS_ZERO);
10429           }
10430         }
10431       }
10432
10433       break;
10434     }
10435
10436     case CA_SET_CE_ARTWORK:
10437     {
10438       int artwork_element = action_arg_element;
10439       boolean reset_frame = FALSE;
10440       int xx, yy;
10441
10442       if (action_arg == CA_ARG_ELEMENT_RESET)
10443         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10444                            element);
10445
10446       if (ei->gfx_element != artwork_element)
10447         reset_frame = TRUE;
10448
10449       ei->gfx_element = artwork_element;
10450
10451       SCAN_PLAYFIELD(xx, yy)
10452       {
10453         if (Tile[xx][yy] == element)
10454         {
10455           if (reset_frame)
10456           {
10457             ResetGfxAnimation(xx, yy);
10458             ResetRandomAnimationValue(xx, yy);
10459           }
10460
10461           TEST_DrawLevelField(xx, yy);
10462         }
10463       }
10464
10465       break;
10466     }
10467
10468     // ---------- engine actions  ---------------------------------------------
10469
10470     case CA_SET_ENGINE_SCAN_MODE:
10471     {
10472       InitPlayfieldScanMode(action_arg);
10473
10474       break;
10475     }
10476
10477     default:
10478       break;
10479   }
10480 }
10481
10482 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10483 {
10484   int old_element = Tile[x][y];
10485   int new_element = GetElementFromGroupElement(element);
10486   int previous_move_direction = MovDir[x][y];
10487   int last_ce_value = CustomValue[x][y];
10488   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10489   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10490   boolean add_player_onto_element = (new_element_is_player &&
10491                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10492                                      IS_WALKABLE(old_element));
10493
10494   if (!add_player_onto_element)
10495   {
10496     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10497       RemoveMovingField(x, y);
10498     else
10499       RemoveField(x, y);
10500
10501     Tile[x][y] = new_element;
10502
10503     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10504       MovDir[x][y] = previous_move_direction;
10505
10506     if (element_info[new_element].use_last_ce_value)
10507       CustomValue[x][y] = last_ce_value;
10508
10509     InitField_WithBug1(x, y, FALSE);
10510
10511     new_element = Tile[x][y];   // element may have changed
10512
10513     ResetGfxAnimation(x, y);
10514     ResetRandomAnimationValue(x, y);
10515
10516     TEST_DrawLevelField(x, y);
10517
10518     if (GFX_CRUMBLED(new_element))
10519       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10520   }
10521
10522   // check if element under the player changes from accessible to unaccessible
10523   // (needed for special case of dropping element which then changes)
10524   // (must be checked after creating new element for walkable group elements)
10525   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10526       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10527   {
10528     Bang(x, y);
10529
10530     return;
10531   }
10532
10533   // "ChangeCount" not set yet to allow "entered by player" change one time
10534   if (new_element_is_player)
10535     RelocatePlayer(x, y, new_element);
10536
10537   if (is_change)
10538     ChangeCount[x][y]++;        // count number of changes in the same frame
10539
10540   TestIfBadThingTouchesPlayer(x, y);
10541   TestIfPlayerTouchesCustomElement(x, y);
10542   TestIfElementTouchesCustomElement(x, y);
10543 }
10544
10545 static void CreateField(int x, int y, int element)
10546 {
10547   CreateFieldExt(x, y, element, FALSE);
10548 }
10549
10550 static void CreateElementFromChange(int x, int y, int element)
10551 {
10552   element = GET_VALID_RUNTIME_ELEMENT(element);
10553
10554   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10555   {
10556     int old_element = Tile[x][y];
10557
10558     // prevent changed element from moving in same engine frame
10559     // unless both old and new element can either fall or move
10560     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10561         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10562       Stop[x][y] = TRUE;
10563   }
10564
10565   CreateFieldExt(x, y, element, TRUE);
10566 }
10567
10568 static boolean ChangeElement(int x, int y, int element, int page)
10569 {
10570   struct ElementInfo *ei = &element_info[element];
10571   struct ElementChangeInfo *change = &ei->change_page[page];
10572   int ce_value = CustomValue[x][y];
10573   int ce_score = ei->collect_score;
10574   int target_element;
10575   int old_element = Tile[x][y];
10576
10577   // always use default change event to prevent running into a loop
10578   if (ChangeEvent[x][y] == -1)
10579     ChangeEvent[x][y] = CE_DELAY;
10580
10581   if (ChangeEvent[x][y] == CE_DELAY)
10582   {
10583     // reset actual trigger element, trigger player and action element
10584     change->actual_trigger_element = EL_EMPTY;
10585     change->actual_trigger_player = EL_EMPTY;
10586     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10587     change->actual_trigger_side = CH_SIDE_NONE;
10588     change->actual_trigger_ce_value = 0;
10589     change->actual_trigger_ce_score = 0;
10590   }
10591
10592   // do not change elements more than a specified maximum number of changes
10593   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10594     return FALSE;
10595
10596   ChangeCount[x][y]++;          // count number of changes in the same frame
10597
10598   if (change->explode)
10599   {
10600     Bang(x, y);
10601
10602     return TRUE;
10603   }
10604
10605   if (change->use_target_content)
10606   {
10607     boolean complete_replace = TRUE;
10608     boolean can_replace[3][3];
10609     int xx, yy;
10610
10611     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10612     {
10613       boolean is_empty;
10614       boolean is_walkable;
10615       boolean is_diggable;
10616       boolean is_collectible;
10617       boolean is_removable;
10618       boolean is_destructible;
10619       int ex = x + xx - 1;
10620       int ey = y + yy - 1;
10621       int content_element = change->target_content.e[xx][yy];
10622       int e;
10623
10624       can_replace[xx][yy] = TRUE;
10625
10626       if (ex == x && ey == y)   // do not check changing element itself
10627         continue;
10628
10629       if (content_element == EL_EMPTY_SPACE)
10630       {
10631         can_replace[xx][yy] = FALSE;    // do not replace border with space
10632
10633         continue;
10634       }
10635
10636       if (!IN_LEV_FIELD(ex, ey))
10637       {
10638         can_replace[xx][yy] = FALSE;
10639         complete_replace = FALSE;
10640
10641         continue;
10642       }
10643
10644       e = Tile[ex][ey];
10645
10646       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10647         e = MovingOrBlocked2Element(ex, ey);
10648
10649       is_empty = (IS_FREE(ex, ey) ||
10650                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10651
10652       is_walkable     = (is_empty || IS_WALKABLE(e));
10653       is_diggable     = (is_empty || IS_DIGGABLE(e));
10654       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10655       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10656       is_removable    = (is_diggable || is_collectible);
10657
10658       can_replace[xx][yy] =
10659         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10660           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10661           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10662           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10663           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10664           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10665          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10666
10667       if (!can_replace[xx][yy])
10668         complete_replace = FALSE;
10669     }
10670
10671     if (!change->only_if_complete || complete_replace)
10672     {
10673       boolean something_has_changed = FALSE;
10674
10675       if (change->only_if_complete && change->use_random_replace &&
10676           RND(100) < change->random_percentage)
10677         return FALSE;
10678
10679       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10680       {
10681         int ex = x + xx - 1;
10682         int ey = y + yy - 1;
10683         int content_element;
10684
10685         if (can_replace[xx][yy] && (!change->use_random_replace ||
10686                                     RND(100) < change->random_percentage))
10687         {
10688           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10689             RemoveMovingField(ex, ey);
10690
10691           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10692
10693           content_element = change->target_content.e[xx][yy];
10694           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10695                                               ce_value, ce_score);
10696
10697           CreateElementFromChange(ex, ey, target_element);
10698
10699           something_has_changed = TRUE;
10700
10701           // for symmetry reasons, freeze newly created border elements
10702           if (ex != x || ey != y)
10703             Stop[ex][ey] = TRUE;        // no more moving in this frame
10704         }
10705       }
10706
10707       if (something_has_changed)
10708       {
10709         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10710         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10711       }
10712     }
10713   }
10714   else
10715   {
10716     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10717                                         ce_value, ce_score);
10718
10719     if (element == EL_DIAGONAL_GROWING ||
10720         element == EL_DIAGONAL_SHRINKING)
10721     {
10722       target_element = Store[x][y];
10723
10724       Store[x][y] = EL_EMPTY;
10725     }
10726
10727     CreateElementFromChange(x, y, target_element);
10728
10729     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10730     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10731   }
10732
10733   // this uses direct change before indirect change
10734   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10735
10736   return TRUE;
10737 }
10738
10739 static void HandleElementChange(int x, int y, int page)
10740 {
10741   int element = MovingOrBlocked2Element(x, y);
10742   struct ElementInfo *ei = &element_info[element];
10743   struct ElementChangeInfo *change = &ei->change_page[page];
10744   boolean handle_action_before_change = FALSE;
10745
10746 #ifdef DEBUG
10747   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10748       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10749   {
10750     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10751           x, y, element, element_info[element].token_name);
10752     Debug("game:playing:HandleElementChange", "This should never happen!");
10753   }
10754 #endif
10755
10756   // this can happen with classic bombs on walkable, changing elements
10757   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10758   {
10759     return;
10760   }
10761
10762   if (ChangeDelay[x][y] == 0)           // initialize element change
10763   {
10764     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10765
10766     if (change->can_change)
10767     {
10768       // !!! not clear why graphic animation should be reset at all here !!!
10769       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10770       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10771
10772       /*
10773         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10774
10775         When using an animation frame delay of 1 (this only happens with
10776         "sp_zonk.moving.left/right" in the classic graphics), the default
10777         (non-moving) animation shows wrong animation frames (while the
10778         moving animation, like "sp_zonk.moving.left/right", is correct,
10779         so this graphical bug never shows up with the classic graphics).
10780         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10781         be drawn instead of the correct frames 0,1,2,3. This is caused by
10782         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10783         an element change: First when the change delay ("ChangeDelay[][]")
10784         counter has reached zero after decrementing, then a second time in
10785         the next frame (after "GfxFrame[][]" was already incremented) when
10786         "ChangeDelay[][]" is reset to the initial delay value again.
10787
10788         This causes frame 0 to be drawn twice, while the last frame won't
10789         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10790
10791         As some animations may already be cleverly designed around this bug
10792         (at least the "Snake Bite" snake tail animation does this), it cannot
10793         simply be fixed here without breaking such existing animations.
10794         Unfortunately, it cannot easily be detected if a graphics set was
10795         designed "before" or "after" the bug was fixed. As a workaround,
10796         a new graphics set option "game.graphics_engine_version" was added
10797         to be able to specify the game's major release version for which the
10798         graphics set was designed, which can then be used to decide if the
10799         bugfix should be used (version 4 and above) or not (version 3 or
10800         below, or if no version was specified at all, as with old sets).
10801
10802         (The wrong/fixed animation frames can be tested with the test level set
10803         "test_gfxframe" and level "000", which contains a specially prepared
10804         custom element at level position (x/y) == (11/9) which uses the zonk
10805         animation mentioned above. Using "game.graphics_engine_version: 4"
10806         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10807         This can also be seen from the debug output for this test element.)
10808       */
10809
10810       // when a custom element is about to change (for example by change delay),
10811       // do not reset graphic animation when the custom element is moving
10812       if (game.graphics_engine_version < 4 &&
10813           !IS_MOVING(x, y))
10814       {
10815         ResetGfxAnimation(x, y);
10816         ResetRandomAnimationValue(x, y);
10817       }
10818
10819       if (change->pre_change_function)
10820         change->pre_change_function(x, y);
10821     }
10822   }
10823
10824   ChangeDelay[x][y]--;
10825
10826   if (ChangeDelay[x][y] != 0)           // continue element change
10827   {
10828     if (change->can_change)
10829     {
10830       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10831
10832       if (IS_ANIMATED(graphic))
10833         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10834
10835       if (change->change_function)
10836         change->change_function(x, y);
10837     }
10838   }
10839   else                                  // finish element change
10840   {
10841     if (ChangePage[x][y] != -1)         // remember page from delayed change
10842     {
10843       page = ChangePage[x][y];
10844       ChangePage[x][y] = -1;
10845
10846       change = &ei->change_page[page];
10847     }
10848
10849     if (IS_MOVING(x, y))                // never change a running system ;-)
10850     {
10851       ChangeDelay[x][y] = 1;            // try change after next move step
10852       ChangePage[x][y] = page;          // remember page to use for change
10853
10854       return;
10855     }
10856
10857     // special case: set new level random seed before changing element
10858     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10859       handle_action_before_change = TRUE;
10860
10861     if (change->has_action && handle_action_before_change)
10862       ExecuteCustomElementAction(x, y, element, page);
10863
10864     if (change->can_change)
10865     {
10866       if (ChangeElement(x, y, element, page))
10867       {
10868         if (change->post_change_function)
10869           change->post_change_function(x, y);
10870       }
10871     }
10872
10873     if (change->has_action && !handle_action_before_change)
10874       ExecuteCustomElementAction(x, y, element, page);
10875   }
10876 }
10877
10878 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10879                                               int trigger_element,
10880                                               int trigger_event,
10881                                               int trigger_player,
10882                                               int trigger_side,
10883                                               int trigger_page)
10884 {
10885   boolean change_done_any = FALSE;
10886   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10887   int i;
10888
10889   if (!(trigger_events[trigger_element][trigger_event]))
10890     return FALSE;
10891
10892   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10893
10894   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10895   {
10896     int element = EL_CUSTOM_START + i;
10897     boolean change_done = FALSE;
10898     int p;
10899
10900     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10901         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10902       continue;
10903
10904     for (p = 0; p < element_info[element].num_change_pages; p++)
10905     {
10906       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10907
10908       if (change->can_change_or_has_action &&
10909           change->has_event[trigger_event] &&
10910           change->trigger_side & trigger_side &&
10911           change->trigger_player & trigger_player &&
10912           change->trigger_page & trigger_page_bits &&
10913           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10914       {
10915         change->actual_trigger_element = trigger_element;
10916         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10917         change->actual_trigger_player_bits = trigger_player;
10918         change->actual_trigger_side = trigger_side;
10919         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10920         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10921
10922         if ((change->can_change && !change_done) || change->has_action)
10923         {
10924           int x, y;
10925
10926           SCAN_PLAYFIELD(x, y)
10927           {
10928             if (Tile[x][y] == element)
10929             {
10930               if (change->can_change && !change_done)
10931               {
10932                 // if element already changed in this frame, not only prevent
10933                 // another element change (checked in ChangeElement()), but
10934                 // also prevent additional element actions for this element
10935
10936                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10937                     !level.use_action_after_change_bug)
10938                   continue;
10939
10940                 ChangeDelay[x][y] = 1;
10941                 ChangeEvent[x][y] = trigger_event;
10942
10943                 HandleElementChange(x, y, p);
10944               }
10945               else if (change->has_action)
10946               {
10947                 // if element already changed in this frame, not only prevent
10948                 // another element change (checked in ChangeElement()), but
10949                 // also prevent additional element actions for this element
10950
10951                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10952                     !level.use_action_after_change_bug)
10953                   continue;
10954
10955                 ExecuteCustomElementAction(x, y, element, p);
10956                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10957               }
10958             }
10959           }
10960
10961           if (change->can_change)
10962           {
10963             change_done = TRUE;
10964             change_done_any = TRUE;
10965           }
10966         }
10967       }
10968     }
10969   }
10970
10971   RECURSION_LOOP_DETECTION_END();
10972
10973   return change_done_any;
10974 }
10975
10976 static boolean CheckElementChangeExt(int x, int y,
10977                                      int element,
10978                                      int trigger_element,
10979                                      int trigger_event,
10980                                      int trigger_player,
10981                                      int trigger_side)
10982 {
10983   boolean change_done = FALSE;
10984   int p;
10985
10986   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10987       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10988     return FALSE;
10989
10990   if (Tile[x][y] == EL_BLOCKED)
10991   {
10992     Blocked2Moving(x, y, &x, &y);
10993     element = Tile[x][y];
10994   }
10995
10996   // check if element has already changed or is about to change after moving
10997   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10998        Tile[x][y] != element) ||
10999
11000       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11001        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11002         ChangePage[x][y] != -1)))
11003     return FALSE;
11004
11005   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11006
11007   for (p = 0; p < element_info[element].num_change_pages; p++)
11008   {
11009     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11010
11011     /* check trigger element for all events where the element that is checked
11012        for changing interacts with a directly adjacent element -- this is
11013        different to element changes that affect other elements to change on the
11014        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11015     boolean check_trigger_element =
11016       (trigger_event == CE_TOUCHING_X ||
11017        trigger_event == CE_HITTING_X ||
11018        trigger_event == CE_HIT_BY_X ||
11019        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11020
11021     if (change->can_change_or_has_action &&
11022         change->has_event[trigger_event] &&
11023         change->trigger_side & trigger_side &&
11024         change->trigger_player & trigger_player &&
11025         (!check_trigger_element ||
11026          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11027     {
11028       change->actual_trigger_element = trigger_element;
11029       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11030       change->actual_trigger_player_bits = trigger_player;
11031       change->actual_trigger_side = trigger_side;
11032       change->actual_trigger_ce_value = CustomValue[x][y];
11033       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11034
11035       // special case: trigger element not at (x,y) position for some events
11036       if (check_trigger_element)
11037       {
11038         static struct
11039         {
11040           int dx, dy;
11041         } move_xy[] =
11042           {
11043             {  0,  0 },
11044             { -1,  0 },
11045             { +1,  0 },
11046             {  0,  0 },
11047             {  0, -1 },
11048             {  0,  0 }, { 0, 0 }, { 0, 0 },
11049             {  0, +1 }
11050           };
11051
11052         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11053         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11054
11055         change->actual_trigger_ce_value = CustomValue[xx][yy];
11056         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11057       }
11058
11059       if (change->can_change && !change_done)
11060       {
11061         ChangeDelay[x][y] = 1;
11062         ChangeEvent[x][y] = trigger_event;
11063
11064         HandleElementChange(x, y, p);
11065
11066         change_done = TRUE;
11067       }
11068       else if (change->has_action)
11069       {
11070         ExecuteCustomElementAction(x, y, element, p);
11071         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11072       }
11073     }
11074   }
11075
11076   RECURSION_LOOP_DETECTION_END();
11077
11078   return change_done;
11079 }
11080
11081 static void PlayPlayerSound(struct PlayerInfo *player)
11082 {
11083   int jx = player->jx, jy = player->jy;
11084   int sound_element = player->artwork_element;
11085   int last_action = player->last_action_waiting;
11086   int action = player->action_waiting;
11087
11088   if (player->is_waiting)
11089   {
11090     if (action != last_action)
11091       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11092     else
11093       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11094   }
11095   else
11096   {
11097     if (action != last_action)
11098       StopSound(element_info[sound_element].sound[last_action]);
11099
11100     if (last_action == ACTION_SLEEPING)
11101       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11102   }
11103 }
11104
11105 static void PlayAllPlayersSound(void)
11106 {
11107   int i;
11108
11109   for (i = 0; i < MAX_PLAYERS; i++)
11110     if (stored_player[i].active)
11111       PlayPlayerSound(&stored_player[i]);
11112 }
11113
11114 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11115 {
11116   boolean last_waiting = player->is_waiting;
11117   int move_dir = player->MovDir;
11118
11119   player->dir_waiting = move_dir;
11120   player->last_action_waiting = player->action_waiting;
11121
11122   if (is_waiting)
11123   {
11124     if (!last_waiting)          // not waiting -> waiting
11125     {
11126       player->is_waiting = TRUE;
11127
11128       player->frame_counter_bored =
11129         FrameCounter +
11130         game.player_boring_delay_fixed +
11131         GetSimpleRandom(game.player_boring_delay_random);
11132       player->frame_counter_sleeping =
11133         FrameCounter +
11134         game.player_sleeping_delay_fixed +
11135         GetSimpleRandom(game.player_sleeping_delay_random);
11136
11137       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11138     }
11139
11140     if (game.player_sleeping_delay_fixed +
11141         game.player_sleeping_delay_random > 0 &&
11142         player->anim_delay_counter == 0 &&
11143         player->post_delay_counter == 0 &&
11144         FrameCounter >= player->frame_counter_sleeping)
11145       player->is_sleeping = TRUE;
11146     else if (game.player_boring_delay_fixed +
11147              game.player_boring_delay_random > 0 &&
11148              FrameCounter >= player->frame_counter_bored)
11149       player->is_bored = TRUE;
11150
11151     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11152                               player->is_bored ? ACTION_BORING :
11153                               ACTION_WAITING);
11154
11155     if (player->is_sleeping && player->use_murphy)
11156     {
11157       // special case for sleeping Murphy when leaning against non-free tile
11158
11159       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11160           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11161            !IS_MOVING(player->jx - 1, player->jy)))
11162         move_dir = MV_LEFT;
11163       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11164                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11165                 !IS_MOVING(player->jx + 1, player->jy)))
11166         move_dir = MV_RIGHT;
11167       else
11168         player->is_sleeping = FALSE;
11169
11170       player->dir_waiting = move_dir;
11171     }
11172
11173     if (player->is_sleeping)
11174     {
11175       if (player->num_special_action_sleeping > 0)
11176       {
11177         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11178         {
11179           int last_special_action = player->special_action_sleeping;
11180           int num_special_action = player->num_special_action_sleeping;
11181           int special_action =
11182             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11183              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11184              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11185              last_special_action + 1 : ACTION_SLEEPING);
11186           int special_graphic =
11187             el_act_dir2img(player->artwork_element, special_action, move_dir);
11188
11189           player->anim_delay_counter =
11190             graphic_info[special_graphic].anim_delay_fixed +
11191             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11192           player->post_delay_counter =
11193             graphic_info[special_graphic].post_delay_fixed +
11194             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11195
11196           player->special_action_sleeping = special_action;
11197         }
11198
11199         if (player->anim_delay_counter > 0)
11200         {
11201           player->action_waiting = player->special_action_sleeping;
11202           player->anim_delay_counter--;
11203         }
11204         else if (player->post_delay_counter > 0)
11205         {
11206           player->post_delay_counter--;
11207         }
11208       }
11209     }
11210     else if (player->is_bored)
11211     {
11212       if (player->num_special_action_bored > 0)
11213       {
11214         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11215         {
11216           int special_action =
11217             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11218           int special_graphic =
11219             el_act_dir2img(player->artwork_element, special_action, move_dir);
11220
11221           player->anim_delay_counter =
11222             graphic_info[special_graphic].anim_delay_fixed +
11223             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11224           player->post_delay_counter =
11225             graphic_info[special_graphic].post_delay_fixed +
11226             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11227
11228           player->special_action_bored = special_action;
11229         }
11230
11231         if (player->anim_delay_counter > 0)
11232         {
11233           player->action_waiting = player->special_action_bored;
11234           player->anim_delay_counter--;
11235         }
11236         else if (player->post_delay_counter > 0)
11237         {
11238           player->post_delay_counter--;
11239         }
11240       }
11241     }
11242   }
11243   else if (last_waiting)        // waiting -> not waiting
11244   {
11245     player->is_waiting = FALSE;
11246     player->is_bored = FALSE;
11247     player->is_sleeping = FALSE;
11248
11249     player->frame_counter_bored = -1;
11250     player->frame_counter_sleeping = -1;
11251
11252     player->anim_delay_counter = 0;
11253     player->post_delay_counter = 0;
11254
11255     player->dir_waiting = player->MovDir;
11256     player->action_waiting = ACTION_DEFAULT;
11257
11258     player->special_action_bored = ACTION_DEFAULT;
11259     player->special_action_sleeping = ACTION_DEFAULT;
11260   }
11261 }
11262
11263 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11264 {
11265   if ((!player->is_moving  && player->was_moving) ||
11266       (player->MovPos == 0 && player->was_moving) ||
11267       (player->is_snapping && !player->was_snapping) ||
11268       (player->is_dropping && !player->was_dropping))
11269   {
11270     if (!CheckSaveEngineSnapshotToList())
11271       return;
11272
11273     player->was_moving = FALSE;
11274     player->was_snapping = TRUE;
11275     player->was_dropping = TRUE;
11276   }
11277   else
11278   {
11279     if (player->is_moving)
11280       player->was_moving = TRUE;
11281
11282     if (!player->is_snapping)
11283       player->was_snapping = FALSE;
11284
11285     if (!player->is_dropping)
11286       player->was_dropping = FALSE;
11287   }
11288
11289   static struct MouseActionInfo mouse_action_last = { 0 };
11290   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11291   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11292
11293   if (new_released)
11294     CheckSaveEngineSnapshotToList();
11295
11296   mouse_action_last = mouse_action;
11297 }
11298
11299 static void CheckSingleStepMode(struct PlayerInfo *player)
11300 {
11301   if (tape.single_step && tape.recording && !tape.pausing)
11302   {
11303     // as it is called "single step mode", just return to pause mode when the
11304     // player stopped moving after one tile (or never starts moving at all)
11305     // (reverse logic needed here in case single step mode used in team mode)
11306     if (player->is_moving ||
11307         player->is_pushing ||
11308         player->is_dropping_pressed ||
11309         player->effective_mouse_action.button)
11310       game.enter_single_step_mode = FALSE;
11311   }
11312
11313   CheckSaveEngineSnapshot(player);
11314 }
11315
11316 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11317 {
11318   int left      = player_action & JOY_LEFT;
11319   int right     = player_action & JOY_RIGHT;
11320   int up        = player_action & JOY_UP;
11321   int down      = player_action & JOY_DOWN;
11322   int button1   = player_action & JOY_BUTTON_1;
11323   int button2   = player_action & JOY_BUTTON_2;
11324   int dx        = (left ? -1 : right ? 1 : 0);
11325   int dy        = (up   ? -1 : down  ? 1 : 0);
11326
11327   if (!player->active || tape.pausing)
11328     return 0;
11329
11330   if (player_action)
11331   {
11332     if (button1)
11333       SnapField(player, dx, dy);
11334     else
11335     {
11336       if (button2)
11337         DropElement(player);
11338
11339       MovePlayer(player, dx, dy);
11340     }
11341
11342     CheckSingleStepMode(player);
11343
11344     SetPlayerWaiting(player, FALSE);
11345
11346     return player_action;
11347   }
11348   else
11349   {
11350     // no actions for this player (no input at player's configured device)
11351
11352     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11353     SnapField(player, 0, 0);
11354     CheckGravityMovementWhenNotMoving(player);
11355
11356     if (player->MovPos == 0)
11357       SetPlayerWaiting(player, TRUE);
11358
11359     if (player->MovPos == 0)    // needed for tape.playing
11360       player->is_moving = FALSE;
11361
11362     player->is_dropping = FALSE;
11363     player->is_dropping_pressed = FALSE;
11364     player->drop_pressed_delay = 0;
11365
11366     CheckSingleStepMode(player);
11367
11368     return 0;
11369   }
11370 }
11371
11372 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11373                                          byte *tape_action)
11374 {
11375   if (!tape.use_mouse_actions)
11376     return;
11377
11378   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11379   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11380   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11381 }
11382
11383 static void SetTapeActionFromMouseAction(byte *tape_action,
11384                                          struct MouseActionInfo *mouse_action)
11385 {
11386   if (!tape.use_mouse_actions)
11387     return;
11388
11389   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11390   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11391   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11392 }
11393
11394 static void CheckLevelSolved(void)
11395 {
11396   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11397   {
11398     if (game_em.level_solved &&
11399         !game_em.game_over)                             // game won
11400     {
11401       LevelSolved();
11402
11403       game_em.game_over = TRUE;
11404
11405       game.all_players_gone = TRUE;
11406     }
11407
11408     if (game_em.game_over)                              // game lost
11409       game.all_players_gone = TRUE;
11410   }
11411   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11412   {
11413     if (game_sp.level_solved &&
11414         !game_sp.game_over)                             // game won
11415     {
11416       LevelSolved();
11417
11418       game_sp.game_over = TRUE;
11419
11420       game.all_players_gone = TRUE;
11421     }
11422
11423     if (game_sp.game_over)                              // game lost
11424       game.all_players_gone = TRUE;
11425   }
11426   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11427   {
11428     if (game_mm.level_solved &&
11429         !game_mm.game_over)                             // game won
11430     {
11431       LevelSolved();
11432
11433       game_mm.game_over = TRUE;
11434
11435       game.all_players_gone = TRUE;
11436     }
11437
11438     if (game_mm.game_over)                              // game lost
11439       game.all_players_gone = TRUE;
11440   }
11441 }
11442
11443 static void CheckLevelTime(void)
11444 {
11445   int i;
11446
11447   if (TimeFrames >= FRAMES_PER_SECOND)
11448   {
11449     TimeFrames = 0;
11450     TapeTime++;
11451
11452     for (i = 0; i < MAX_PLAYERS; i++)
11453     {
11454       struct PlayerInfo *player = &stored_player[i];
11455
11456       if (SHIELD_ON(player))
11457       {
11458         player->shield_normal_time_left--;
11459
11460         if (player->shield_deadly_time_left > 0)
11461           player->shield_deadly_time_left--;
11462       }
11463     }
11464
11465     if (!game.LevelSolved && !level.use_step_counter)
11466     {
11467       TimePlayed++;
11468
11469       if (TimeLeft > 0)
11470       {
11471         TimeLeft--;
11472
11473         if (TimeLeft <= 10 && setup.time_limit)
11474           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11475
11476         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11477            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11478
11479         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11480
11481         if (!TimeLeft && setup.time_limit)
11482         {
11483           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11484             game_em.lev->killed_out_of_time = TRUE;
11485           else
11486             for (i = 0; i < MAX_PLAYERS; i++)
11487               KillPlayer(&stored_player[i]);
11488         }
11489       }
11490       else if (game.no_time_limit && !game.all_players_gone)
11491       {
11492         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11493       }
11494
11495       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11496     }
11497
11498     if (tape.recording || tape.playing)
11499       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11500   }
11501
11502   if (tape.recording || tape.playing)
11503     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11504
11505   UpdateAndDisplayGameControlValues();
11506 }
11507
11508 void AdvanceFrameAndPlayerCounters(int player_nr)
11509 {
11510   int i;
11511
11512   // advance frame counters (global frame counter and time frame counter)
11513   FrameCounter++;
11514   TimeFrames++;
11515
11516   // advance player counters (counters for move delay, move animation etc.)
11517   for (i = 0; i < MAX_PLAYERS; i++)
11518   {
11519     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11520     int move_delay_value = stored_player[i].move_delay_value;
11521     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11522
11523     if (!advance_player_counters)       // not all players may be affected
11524       continue;
11525
11526     if (move_frames == 0)       // less than one move per game frame
11527     {
11528       int stepsize = TILEX / move_delay_value;
11529       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11530       int count = (stored_player[i].is_moving ?
11531                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11532
11533       if (count % delay == 0)
11534         move_frames = 1;
11535     }
11536
11537     stored_player[i].Frame += move_frames;
11538
11539     if (stored_player[i].MovPos != 0)
11540       stored_player[i].StepFrame += move_frames;
11541
11542     if (stored_player[i].move_delay > 0)
11543       stored_player[i].move_delay--;
11544
11545     // due to bugs in previous versions, counter must count up, not down
11546     if (stored_player[i].push_delay != -1)
11547       stored_player[i].push_delay++;
11548
11549     if (stored_player[i].drop_delay > 0)
11550       stored_player[i].drop_delay--;
11551
11552     if (stored_player[i].is_dropping_pressed)
11553       stored_player[i].drop_pressed_delay++;
11554   }
11555 }
11556
11557 void StartGameActions(boolean init_network_game, boolean record_tape,
11558                       int random_seed)
11559 {
11560   unsigned int new_random_seed = InitRND(random_seed);
11561
11562   if (record_tape)
11563     TapeStartRecording(new_random_seed);
11564
11565   if (init_network_game)
11566   {
11567     SendToServer_LevelFile();
11568     SendToServer_StartPlaying();
11569
11570     return;
11571   }
11572
11573   InitGame();
11574 }
11575
11576 static void GameActionsExt(void)
11577 {
11578 #if 0
11579   static unsigned int game_frame_delay = 0;
11580 #endif
11581   unsigned int game_frame_delay_value;
11582   byte *recorded_player_action;
11583   byte summarized_player_action = 0;
11584   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11585   int i;
11586
11587   // detect endless loops, caused by custom element programming
11588   if (recursion_loop_detected && recursion_loop_depth == 0)
11589   {
11590     char *message = getStringCat3("Internal Error! Element ",
11591                                   EL_NAME(recursion_loop_element),
11592                                   " caused endless loop! Quit the game?");
11593
11594     Warn("element '%s' caused endless loop in game engine",
11595          EL_NAME(recursion_loop_element));
11596
11597     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11598
11599     recursion_loop_detected = FALSE;    // if game should be continued
11600
11601     free(message);
11602
11603     return;
11604   }
11605
11606   if (game.restart_level)
11607     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11608
11609   CheckLevelSolved();
11610
11611   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11612     GameWon();
11613
11614   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11615     TapeStop();
11616
11617   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11618     return;
11619
11620   game_frame_delay_value =
11621     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11622
11623   if (tape.playing && tape.warp_forward && !tape.pausing)
11624     game_frame_delay_value = 0;
11625
11626   SetVideoFrameDelay(game_frame_delay_value);
11627
11628   // (de)activate virtual buttons depending on current game status
11629   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11630   {
11631     if (game.all_players_gone)  // if no players there to be controlled anymore
11632       SetOverlayActive(FALSE);
11633     else if (!tape.playing)     // if game continues after tape stopped playing
11634       SetOverlayActive(TRUE);
11635   }
11636
11637 #if 0
11638 #if 0
11639   // ---------- main game synchronization point ----------
11640
11641   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11642
11643   Debug("game:playing:skip", "skip == %d", skip);
11644
11645 #else
11646   // ---------- main game synchronization point ----------
11647
11648   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11649 #endif
11650 #endif
11651
11652   if (network_playing && !network_player_action_received)
11653   {
11654     // try to get network player actions in time
11655
11656     // last chance to get network player actions without main loop delay
11657     HandleNetworking();
11658
11659     // game was quit by network peer
11660     if (game_status != GAME_MODE_PLAYING)
11661       return;
11662
11663     // check if network player actions still missing and game still running
11664     if (!network_player_action_received && !checkGameEnded())
11665       return;           // failed to get network player actions in time
11666
11667     // do not yet reset "network_player_action_received" (for tape.pausing)
11668   }
11669
11670   if (tape.pausing)
11671     return;
11672
11673   // at this point we know that we really continue executing the game
11674
11675   network_player_action_received = FALSE;
11676
11677   // when playing tape, read previously recorded player input from tape data
11678   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11679
11680   local_player->effective_mouse_action = local_player->mouse_action;
11681
11682   if (recorded_player_action != NULL)
11683     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11684                                  recorded_player_action);
11685
11686   // TapePlayAction() may return NULL when toggling to "pause before death"
11687   if (tape.pausing)
11688     return;
11689
11690   if (tape.set_centered_player)
11691   {
11692     game.centered_player_nr_next = tape.centered_player_nr_next;
11693     game.set_centered_player = TRUE;
11694   }
11695
11696   for (i = 0; i < MAX_PLAYERS; i++)
11697   {
11698     summarized_player_action |= stored_player[i].action;
11699
11700     if (!network_playing && (game.team_mode || tape.playing))
11701       stored_player[i].effective_action = stored_player[i].action;
11702   }
11703
11704   if (network_playing && !checkGameEnded())
11705     SendToServer_MovePlayer(summarized_player_action);
11706
11707   // summarize all actions at local players mapped input device position
11708   // (this allows using different input devices in single player mode)
11709   if (!network.enabled && !game.team_mode)
11710     stored_player[map_player_action[local_player->index_nr]].effective_action =
11711       summarized_player_action;
11712
11713   // summarize all actions at centered player in local team mode
11714   if (tape.recording &&
11715       setup.team_mode && !network.enabled &&
11716       setup.input_on_focus &&
11717       game.centered_player_nr != -1)
11718   {
11719     for (i = 0; i < MAX_PLAYERS; i++)
11720       stored_player[map_player_action[i]].effective_action =
11721         (i == game.centered_player_nr ? summarized_player_action : 0);
11722   }
11723
11724   if (recorded_player_action != NULL)
11725     for (i = 0; i < MAX_PLAYERS; i++)
11726       stored_player[i].effective_action = recorded_player_action[i];
11727
11728   for (i = 0; i < MAX_PLAYERS; i++)
11729   {
11730     tape_action[i] = stored_player[i].effective_action;
11731
11732     /* (this may happen in the RND game engine if a player was not present on
11733        the playfield on level start, but appeared later from a custom element */
11734     if (setup.team_mode &&
11735         tape.recording &&
11736         tape_action[i] &&
11737         !tape.player_participates[i])
11738       tape.player_participates[i] = TRUE;
11739   }
11740
11741   SetTapeActionFromMouseAction(tape_action,
11742                                &local_player->effective_mouse_action);
11743
11744   // only record actions from input devices, but not programmed actions
11745   if (tape.recording)
11746     TapeRecordAction(tape_action);
11747
11748   // remember if game was played (especially after tape stopped playing)
11749   if (!tape.playing && summarized_player_action)
11750     game.GamePlayed = TRUE;
11751
11752 #if USE_NEW_PLAYER_ASSIGNMENTS
11753   // !!! also map player actions in single player mode !!!
11754   // if (game.team_mode)
11755   if (1)
11756   {
11757     byte mapped_action[MAX_PLAYERS];
11758
11759 #if DEBUG_PLAYER_ACTIONS
11760     for (i = 0; i < MAX_PLAYERS; i++)
11761       DebugContinued("", "%d, ", stored_player[i].effective_action);
11762 #endif
11763
11764     for (i = 0; i < MAX_PLAYERS; i++)
11765       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11766
11767     for (i = 0; i < MAX_PLAYERS; i++)
11768       stored_player[i].effective_action = mapped_action[i];
11769
11770 #if DEBUG_PLAYER_ACTIONS
11771     DebugContinued("", "=> ");
11772     for (i = 0; i < MAX_PLAYERS; i++)
11773       DebugContinued("", "%d, ", stored_player[i].effective_action);
11774     DebugContinued("game:playing:player", "\n");
11775 #endif
11776   }
11777 #if DEBUG_PLAYER_ACTIONS
11778   else
11779   {
11780     for (i = 0; i < MAX_PLAYERS; i++)
11781       DebugContinued("", "%d, ", stored_player[i].effective_action);
11782     DebugContinued("game:playing:player", "\n");
11783   }
11784 #endif
11785 #endif
11786
11787   for (i = 0; i < MAX_PLAYERS; i++)
11788   {
11789     // allow engine snapshot in case of changed movement attempt
11790     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11791         (stored_player[i].effective_action & KEY_MOTION))
11792       game.snapshot.changed_action = TRUE;
11793
11794     // allow engine snapshot in case of snapping/dropping attempt
11795     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11796         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11797       game.snapshot.changed_action = TRUE;
11798
11799     game.snapshot.last_action[i] = stored_player[i].effective_action;
11800   }
11801
11802   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11803   {
11804     GameActions_EM_Main();
11805   }
11806   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11807   {
11808     GameActions_SP_Main();
11809   }
11810   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11811   {
11812     GameActions_MM_Main();
11813   }
11814   else
11815   {
11816     GameActions_RND_Main();
11817   }
11818
11819   BlitScreenToBitmap(backbuffer);
11820
11821   CheckLevelSolved();
11822   CheckLevelTime();
11823
11824   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11825
11826   if (global.show_frames_per_second)
11827   {
11828     static unsigned int fps_counter = 0;
11829     static int fps_frames = 0;
11830     unsigned int fps_delay_ms = Counter() - fps_counter;
11831
11832     fps_frames++;
11833
11834     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11835     {
11836       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11837
11838       fps_frames = 0;
11839       fps_counter = Counter();
11840
11841       // always draw FPS to screen after FPS value was updated
11842       redraw_mask |= REDRAW_FPS;
11843     }
11844
11845     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11846     if (GetDrawDeactivationMask() == REDRAW_NONE)
11847       redraw_mask |= REDRAW_FPS;
11848   }
11849 }
11850
11851 static void GameActions_CheckSaveEngineSnapshot(void)
11852 {
11853   if (!game.snapshot.save_snapshot)
11854     return;
11855
11856   // clear flag for saving snapshot _before_ saving snapshot
11857   game.snapshot.save_snapshot = FALSE;
11858
11859   SaveEngineSnapshotToList();
11860 }
11861
11862 void GameActions(void)
11863 {
11864   GameActionsExt();
11865
11866   GameActions_CheckSaveEngineSnapshot();
11867 }
11868
11869 void GameActions_EM_Main(void)
11870 {
11871   byte effective_action[MAX_PLAYERS];
11872   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11873   int i;
11874
11875   for (i = 0; i < MAX_PLAYERS; i++)
11876     effective_action[i] = stored_player[i].effective_action;
11877
11878   GameActions_EM(effective_action, warp_mode);
11879 }
11880
11881 void GameActions_SP_Main(void)
11882 {
11883   byte effective_action[MAX_PLAYERS];
11884   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11885   int i;
11886
11887   for (i = 0; i < MAX_PLAYERS; i++)
11888     effective_action[i] = stored_player[i].effective_action;
11889
11890   GameActions_SP(effective_action, warp_mode);
11891
11892   for (i = 0; i < MAX_PLAYERS; i++)
11893   {
11894     if (stored_player[i].force_dropping)
11895       stored_player[i].action |= KEY_BUTTON_DROP;
11896
11897     stored_player[i].force_dropping = FALSE;
11898   }
11899 }
11900
11901 void GameActions_MM_Main(void)
11902 {
11903   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11904
11905   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11906 }
11907
11908 void GameActions_RND_Main(void)
11909 {
11910   GameActions_RND();
11911 }
11912
11913 void GameActions_RND(void)
11914 {
11915   static struct MouseActionInfo mouse_action_last = { 0 };
11916   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11917   int magic_wall_x = 0, magic_wall_y = 0;
11918   int i, x, y, element, graphic, last_gfx_frame;
11919
11920   InitPlayfieldScanModeVars();
11921
11922   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11923   {
11924     SCAN_PLAYFIELD(x, y)
11925     {
11926       ChangeCount[x][y] = 0;
11927       ChangeEvent[x][y] = -1;
11928     }
11929   }
11930
11931   if (game.set_centered_player)
11932   {
11933     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11934
11935     // switching to "all players" only possible if all players fit to screen
11936     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11937     {
11938       game.centered_player_nr_next = game.centered_player_nr;
11939       game.set_centered_player = FALSE;
11940     }
11941
11942     // do not switch focus to non-existing (or non-active) player
11943     if (game.centered_player_nr_next >= 0 &&
11944         !stored_player[game.centered_player_nr_next].active)
11945     {
11946       game.centered_player_nr_next = game.centered_player_nr;
11947       game.set_centered_player = FALSE;
11948     }
11949   }
11950
11951   if (game.set_centered_player &&
11952       ScreenMovPos == 0)        // screen currently aligned at tile position
11953   {
11954     int sx, sy;
11955
11956     if (game.centered_player_nr_next == -1)
11957     {
11958       setScreenCenteredToAllPlayers(&sx, &sy);
11959     }
11960     else
11961     {
11962       sx = stored_player[game.centered_player_nr_next].jx;
11963       sy = stored_player[game.centered_player_nr_next].jy;
11964     }
11965
11966     game.centered_player_nr = game.centered_player_nr_next;
11967     game.set_centered_player = FALSE;
11968
11969     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11970     DrawGameDoorValues();
11971   }
11972
11973   // check single step mode (set flag and clear again if any player is active)
11974   game.enter_single_step_mode =
11975     (tape.single_step && tape.recording && !tape.pausing);
11976
11977   for (i = 0; i < MAX_PLAYERS; i++)
11978   {
11979     int actual_player_action = stored_player[i].effective_action;
11980
11981 #if 1
11982     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11983        - rnd_equinox_tetrachloride 048
11984        - rnd_equinox_tetrachloride_ii 096
11985        - rnd_emanuel_schmieg 002
11986        - doctor_sloan_ww 001, 020
11987     */
11988     if (stored_player[i].MovPos == 0)
11989       CheckGravityMovement(&stored_player[i]);
11990 #endif
11991
11992     // overwrite programmed action with tape action
11993     if (stored_player[i].programmed_action)
11994       actual_player_action = stored_player[i].programmed_action;
11995
11996     PlayerActions(&stored_player[i], actual_player_action);
11997
11998     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11999   }
12000
12001   // single step pause mode may already have been toggled by "ScrollPlayer()"
12002   if (game.enter_single_step_mode && !tape.pausing)
12003     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12004
12005   ScrollScreen(NULL, SCROLL_GO_ON);
12006
12007   /* for backwards compatibility, the following code emulates a fixed bug that
12008      occured when pushing elements (causing elements that just made their last
12009      pushing step to already (if possible) make their first falling step in the
12010      same game frame, which is bad); this code is also needed to use the famous
12011      "spring push bug" which is used in older levels and might be wanted to be
12012      used also in newer levels, but in this case the buggy pushing code is only
12013      affecting the "spring" element and no other elements */
12014
12015   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12016   {
12017     for (i = 0; i < MAX_PLAYERS; i++)
12018     {
12019       struct PlayerInfo *player = &stored_player[i];
12020       int x = player->jx;
12021       int y = player->jy;
12022
12023       if (player->active && player->is_pushing && player->is_moving &&
12024           IS_MOVING(x, y) &&
12025           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12026            Tile[x][y] == EL_SPRING))
12027       {
12028         ContinueMoving(x, y);
12029
12030         // continue moving after pushing (this is actually a bug)
12031         if (!IS_MOVING(x, y))
12032           Stop[x][y] = FALSE;
12033       }
12034     }
12035   }
12036
12037   SCAN_PLAYFIELD(x, y)
12038   {
12039     Last[x][y] = Tile[x][y];
12040
12041     ChangeCount[x][y] = 0;
12042     ChangeEvent[x][y] = -1;
12043
12044     // this must be handled before main playfield loop
12045     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12046     {
12047       MovDelay[x][y]--;
12048       if (MovDelay[x][y] <= 0)
12049         RemoveField(x, y);
12050     }
12051
12052     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12053     {
12054       MovDelay[x][y]--;
12055       if (MovDelay[x][y] <= 0)
12056       {
12057         int element = Store[x][y];
12058         int move_direction = MovDir[x][y];
12059         int player_index_bit = Store2[x][y];
12060
12061         Store[x][y] = 0;
12062         Store2[x][y] = 0;
12063
12064         RemoveField(x, y);
12065         TEST_DrawLevelField(x, y);
12066
12067         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12068       }
12069     }
12070
12071 #if DEBUG
12072     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12073     {
12074       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12075             x, y);
12076       Debug("game:playing:GameActions_RND", "This should never happen!");
12077
12078       ChangePage[x][y] = -1;
12079     }
12080 #endif
12081
12082     Stop[x][y] = FALSE;
12083     if (WasJustMoving[x][y] > 0)
12084       WasJustMoving[x][y]--;
12085     if (WasJustFalling[x][y] > 0)
12086       WasJustFalling[x][y]--;
12087     if (CheckCollision[x][y] > 0)
12088       CheckCollision[x][y]--;
12089     if (CheckImpact[x][y] > 0)
12090       CheckImpact[x][y]--;
12091
12092     GfxFrame[x][y]++;
12093
12094     /* reset finished pushing action (not done in ContinueMoving() to allow
12095        continuous pushing animation for elements with zero push delay) */
12096     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12097     {
12098       ResetGfxAnimation(x, y);
12099       TEST_DrawLevelField(x, y);
12100     }
12101
12102 #if DEBUG
12103     if (IS_BLOCKED(x, y))
12104     {
12105       int oldx, oldy;
12106
12107       Blocked2Moving(x, y, &oldx, &oldy);
12108       if (!IS_MOVING(oldx, oldy))
12109       {
12110         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12111         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12112         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12113         Debug("game:playing:GameActions_RND", "This should never happen!");
12114       }
12115     }
12116 #endif
12117   }
12118
12119   if (mouse_action.button)
12120   {
12121     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12122
12123     x = mouse_action.lx;
12124     y = mouse_action.ly;
12125     element = Tile[x][y];
12126
12127     if (new_button)
12128     {
12129       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12130       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12131     }
12132
12133     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12134     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12135   }
12136
12137   SCAN_PLAYFIELD(x, y)
12138   {
12139     element = Tile[x][y];
12140     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12141     last_gfx_frame = GfxFrame[x][y];
12142
12143     ResetGfxFrame(x, y);
12144
12145     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12146       DrawLevelGraphicAnimation(x, y, graphic);
12147
12148     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12149         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12150       ResetRandomAnimationValue(x, y);
12151
12152     SetRandomAnimationValue(x, y);
12153
12154     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12155
12156     if (IS_INACTIVE(element))
12157     {
12158       if (IS_ANIMATED(graphic))
12159         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12160
12161       continue;
12162     }
12163
12164     // this may take place after moving, so 'element' may have changed
12165     if (IS_CHANGING(x, y) &&
12166         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12167     {
12168       int page = element_info[element].event_page_nr[CE_DELAY];
12169
12170       HandleElementChange(x, y, page);
12171
12172       element = Tile[x][y];
12173       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12174     }
12175
12176     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12177     {
12178       StartMoving(x, y);
12179
12180       element = Tile[x][y];
12181       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12182
12183       if (IS_ANIMATED(graphic) &&
12184           !IS_MOVING(x, y) &&
12185           !Stop[x][y])
12186         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12187
12188       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12189         TEST_DrawTwinkleOnField(x, y);
12190     }
12191     else if (element == EL_ACID)
12192     {
12193       if (!Stop[x][y])
12194         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12195     }
12196     else if ((element == EL_EXIT_OPEN ||
12197               element == EL_EM_EXIT_OPEN ||
12198               element == EL_SP_EXIT_OPEN ||
12199               element == EL_STEEL_EXIT_OPEN ||
12200               element == EL_EM_STEEL_EXIT_OPEN ||
12201               element == EL_SP_TERMINAL ||
12202               element == EL_SP_TERMINAL_ACTIVE ||
12203               element == EL_EXTRA_TIME ||
12204               element == EL_SHIELD_NORMAL ||
12205               element == EL_SHIELD_DEADLY) &&
12206              IS_ANIMATED(graphic))
12207       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12208     else if (IS_MOVING(x, y))
12209       ContinueMoving(x, y);
12210     else if (IS_ACTIVE_BOMB(element))
12211       CheckDynamite(x, y);
12212     else if (element == EL_AMOEBA_GROWING)
12213       AmoebaGrowing(x, y);
12214     else if (element == EL_AMOEBA_SHRINKING)
12215       AmoebaShrinking(x, y);
12216
12217 #if !USE_NEW_AMOEBA_CODE
12218     else if (IS_AMOEBALIVE(element))
12219       AmoebaReproduce(x, y);
12220 #endif
12221
12222     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12223       Life(x, y);
12224     else if (element == EL_EXIT_CLOSED)
12225       CheckExit(x, y);
12226     else if (element == EL_EM_EXIT_CLOSED)
12227       CheckExitEM(x, y);
12228     else if (element == EL_STEEL_EXIT_CLOSED)
12229       CheckExitSteel(x, y);
12230     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12231       CheckExitSteelEM(x, y);
12232     else if (element == EL_SP_EXIT_CLOSED)
12233       CheckExitSP(x, y);
12234     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12235              element == EL_EXPANDABLE_STEELWALL_GROWING)
12236       MauerWaechst(x, y);
12237     else if (element == EL_EXPANDABLE_WALL ||
12238              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12239              element == EL_EXPANDABLE_WALL_VERTICAL ||
12240              element == EL_EXPANDABLE_WALL_ANY ||
12241              element == EL_BD_EXPANDABLE_WALL)
12242       MauerAbleger(x, y);
12243     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12244              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12245              element == EL_EXPANDABLE_STEELWALL_ANY)
12246       MauerAblegerStahl(x, y);
12247     else if (element == EL_FLAMES)
12248       CheckForDragon(x, y);
12249     else if (element == EL_EXPLOSION)
12250       ; // drawing of correct explosion animation is handled separately
12251     else if (element == EL_ELEMENT_SNAPPING ||
12252              element == EL_DIAGONAL_SHRINKING ||
12253              element == EL_DIAGONAL_GROWING)
12254     {
12255       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12256
12257       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12258     }
12259     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12260       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12261
12262     if (IS_BELT_ACTIVE(element))
12263       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12264
12265     if (game.magic_wall_active)
12266     {
12267       int jx = local_player->jx, jy = local_player->jy;
12268
12269       // play the element sound at the position nearest to the player
12270       if ((element == EL_MAGIC_WALL_FULL ||
12271            element == EL_MAGIC_WALL_ACTIVE ||
12272            element == EL_MAGIC_WALL_EMPTYING ||
12273            element == EL_BD_MAGIC_WALL_FULL ||
12274            element == EL_BD_MAGIC_WALL_ACTIVE ||
12275            element == EL_BD_MAGIC_WALL_EMPTYING ||
12276            element == EL_DC_MAGIC_WALL_FULL ||
12277            element == EL_DC_MAGIC_WALL_ACTIVE ||
12278            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12279           ABS(x - jx) + ABS(y - jy) <
12280           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12281       {
12282         magic_wall_x = x;
12283         magic_wall_y = y;
12284       }
12285     }
12286   }
12287
12288 #if USE_NEW_AMOEBA_CODE
12289   // new experimental amoeba growth stuff
12290   if (!(FrameCounter % 8))
12291   {
12292     static unsigned int random = 1684108901;
12293
12294     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12295     {
12296       x = RND(lev_fieldx);
12297       y = RND(lev_fieldy);
12298       element = Tile[x][y];
12299
12300       if (!IS_PLAYER(x,y) &&
12301           (element == EL_EMPTY ||
12302            CAN_GROW_INTO(element) ||
12303            element == EL_QUICKSAND_EMPTY ||
12304            element == EL_QUICKSAND_FAST_EMPTY ||
12305            element == EL_ACID_SPLASH_LEFT ||
12306            element == EL_ACID_SPLASH_RIGHT))
12307       {
12308         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12309             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12310             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12311             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12312           Tile[x][y] = EL_AMOEBA_DROP;
12313       }
12314
12315       random = random * 129 + 1;
12316     }
12317   }
12318 #endif
12319
12320   game.explosions_delayed = FALSE;
12321
12322   SCAN_PLAYFIELD(x, y)
12323   {
12324     element = Tile[x][y];
12325
12326     if (ExplodeField[x][y])
12327       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12328     else if (element == EL_EXPLOSION)
12329       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12330
12331     ExplodeField[x][y] = EX_TYPE_NONE;
12332   }
12333
12334   game.explosions_delayed = TRUE;
12335
12336   if (game.magic_wall_active)
12337   {
12338     if (!(game.magic_wall_time_left % 4))
12339     {
12340       int element = Tile[magic_wall_x][magic_wall_y];
12341
12342       if (element == EL_BD_MAGIC_WALL_FULL ||
12343           element == EL_BD_MAGIC_WALL_ACTIVE ||
12344           element == EL_BD_MAGIC_WALL_EMPTYING)
12345         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12346       else if (element == EL_DC_MAGIC_WALL_FULL ||
12347                element == EL_DC_MAGIC_WALL_ACTIVE ||
12348                element == EL_DC_MAGIC_WALL_EMPTYING)
12349         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12350       else
12351         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12352     }
12353
12354     if (game.magic_wall_time_left > 0)
12355     {
12356       game.magic_wall_time_left--;
12357
12358       if (!game.magic_wall_time_left)
12359       {
12360         SCAN_PLAYFIELD(x, y)
12361         {
12362           element = Tile[x][y];
12363
12364           if (element == EL_MAGIC_WALL_ACTIVE ||
12365               element == EL_MAGIC_WALL_FULL)
12366           {
12367             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12368             TEST_DrawLevelField(x, y);
12369           }
12370           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12371                    element == EL_BD_MAGIC_WALL_FULL)
12372           {
12373             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12374             TEST_DrawLevelField(x, y);
12375           }
12376           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12377                    element == EL_DC_MAGIC_WALL_FULL)
12378           {
12379             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12380             TEST_DrawLevelField(x, y);
12381           }
12382         }
12383
12384         game.magic_wall_active = FALSE;
12385       }
12386     }
12387   }
12388
12389   if (game.light_time_left > 0)
12390   {
12391     game.light_time_left--;
12392
12393     if (game.light_time_left == 0)
12394       RedrawAllLightSwitchesAndInvisibleElements();
12395   }
12396
12397   if (game.timegate_time_left > 0)
12398   {
12399     game.timegate_time_left--;
12400
12401     if (game.timegate_time_left == 0)
12402       CloseAllOpenTimegates();
12403   }
12404
12405   if (game.lenses_time_left > 0)
12406   {
12407     game.lenses_time_left--;
12408
12409     if (game.lenses_time_left == 0)
12410       RedrawAllInvisibleElementsForLenses();
12411   }
12412
12413   if (game.magnify_time_left > 0)
12414   {
12415     game.magnify_time_left--;
12416
12417     if (game.magnify_time_left == 0)
12418       RedrawAllInvisibleElementsForMagnifier();
12419   }
12420
12421   for (i = 0; i < MAX_PLAYERS; i++)
12422   {
12423     struct PlayerInfo *player = &stored_player[i];
12424
12425     if (SHIELD_ON(player))
12426     {
12427       if (player->shield_deadly_time_left)
12428         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12429       else if (player->shield_normal_time_left)
12430         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12431     }
12432   }
12433
12434 #if USE_DELAYED_GFX_REDRAW
12435   SCAN_PLAYFIELD(x, y)
12436   {
12437     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12438     {
12439       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12440          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12441
12442       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12443         DrawLevelField(x, y);
12444
12445       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12446         DrawLevelFieldCrumbled(x, y);
12447
12448       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12449         DrawLevelFieldCrumbledNeighbours(x, y);
12450
12451       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12452         DrawTwinkleOnField(x, y);
12453     }
12454
12455     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12456   }
12457 #endif
12458
12459   DrawAllPlayers();
12460   PlayAllPlayersSound();
12461
12462   for (i = 0; i < MAX_PLAYERS; i++)
12463   {
12464     struct PlayerInfo *player = &stored_player[i];
12465
12466     if (player->show_envelope != 0 && (!player->active ||
12467                                        player->MovPos == 0))
12468     {
12469       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12470
12471       player->show_envelope = 0;
12472     }
12473   }
12474
12475   // use random number generator in every frame to make it less predictable
12476   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12477     RND(1);
12478
12479   mouse_action_last = mouse_action;
12480 }
12481
12482 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12483 {
12484   int min_x = x, min_y = y, max_x = x, max_y = y;
12485   int scr_fieldx = getScreenFieldSizeX();
12486   int scr_fieldy = getScreenFieldSizeY();
12487   int i;
12488
12489   for (i = 0; i < MAX_PLAYERS; i++)
12490   {
12491     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12492
12493     if (!stored_player[i].active || &stored_player[i] == player)
12494       continue;
12495
12496     min_x = MIN(min_x, jx);
12497     min_y = MIN(min_y, jy);
12498     max_x = MAX(max_x, jx);
12499     max_y = MAX(max_y, jy);
12500   }
12501
12502   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12503 }
12504
12505 static boolean AllPlayersInVisibleScreen(void)
12506 {
12507   int i;
12508
12509   for (i = 0; i < MAX_PLAYERS; i++)
12510   {
12511     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12512
12513     if (!stored_player[i].active)
12514       continue;
12515
12516     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12517       return FALSE;
12518   }
12519
12520   return TRUE;
12521 }
12522
12523 void ScrollLevel(int dx, int dy)
12524 {
12525   int scroll_offset = 2 * TILEX_VAR;
12526   int x, y;
12527
12528   BlitBitmap(drawto_field, drawto_field,
12529              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12530              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12531              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12532              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12533              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12534              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12535
12536   if (dx != 0)
12537   {
12538     x = (dx == 1 ? BX1 : BX2);
12539     for (y = BY1; y <= BY2; y++)
12540       DrawScreenField(x, y);
12541   }
12542
12543   if (dy != 0)
12544   {
12545     y = (dy == 1 ? BY1 : BY2);
12546     for (x = BX1; x <= BX2; x++)
12547       DrawScreenField(x, y);
12548   }
12549
12550   redraw_mask |= REDRAW_FIELD;
12551 }
12552
12553 static boolean canFallDown(struct PlayerInfo *player)
12554 {
12555   int jx = player->jx, jy = player->jy;
12556
12557   return (IN_LEV_FIELD(jx, jy + 1) &&
12558           (IS_FREE(jx, jy + 1) ||
12559            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12560           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12561           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12562 }
12563
12564 static boolean canPassField(int x, int y, int move_dir)
12565 {
12566   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12567   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12568   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12569   int nextx = x + dx;
12570   int nexty = y + dy;
12571   int element = Tile[x][y];
12572
12573   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12574           !CAN_MOVE(element) &&
12575           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12576           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12577           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12578 }
12579
12580 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12581 {
12582   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12583   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12584   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12585   int newx = x + dx;
12586   int newy = y + dy;
12587
12588   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12589           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12590           (IS_DIGGABLE(Tile[newx][newy]) ||
12591            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12592            canPassField(newx, newy, move_dir)));
12593 }
12594
12595 static void CheckGravityMovement(struct PlayerInfo *player)
12596 {
12597   if (player->gravity && !player->programmed_action)
12598   {
12599     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12600     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12601     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12602     int jx = player->jx, jy = player->jy;
12603     boolean player_is_moving_to_valid_field =
12604       (!player_is_snapping &&
12605        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12606         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12607     boolean player_can_fall_down = canFallDown(player);
12608
12609     if (player_can_fall_down &&
12610         !player_is_moving_to_valid_field)
12611       player->programmed_action = MV_DOWN;
12612   }
12613 }
12614
12615 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12616 {
12617   return CheckGravityMovement(player);
12618
12619   if (player->gravity && !player->programmed_action)
12620   {
12621     int jx = player->jx, jy = player->jy;
12622     boolean field_under_player_is_free =
12623       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12624     boolean player_is_standing_on_valid_field =
12625       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12626        (IS_WALKABLE(Tile[jx][jy]) &&
12627         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12628
12629     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12630       player->programmed_action = MV_DOWN;
12631   }
12632 }
12633
12634 /*
12635   MovePlayerOneStep()
12636   -----------------------------------------------------------------------------
12637   dx, dy:               direction (non-diagonal) to try to move the player to
12638   real_dx, real_dy:     direction as read from input device (can be diagonal)
12639 */
12640
12641 boolean MovePlayerOneStep(struct PlayerInfo *player,
12642                           int dx, int dy, int real_dx, int real_dy)
12643 {
12644   int jx = player->jx, jy = player->jy;
12645   int new_jx = jx + dx, new_jy = jy + dy;
12646   int can_move;
12647   boolean player_can_move = !player->cannot_move;
12648
12649   if (!player->active || (!dx && !dy))
12650     return MP_NO_ACTION;
12651
12652   player->MovDir = (dx < 0 ? MV_LEFT :
12653                     dx > 0 ? MV_RIGHT :
12654                     dy < 0 ? MV_UP :
12655                     dy > 0 ? MV_DOWN :  MV_NONE);
12656
12657   if (!IN_LEV_FIELD(new_jx, new_jy))
12658     return MP_NO_ACTION;
12659
12660   if (!player_can_move)
12661   {
12662     if (player->MovPos == 0)
12663     {
12664       player->is_moving = FALSE;
12665       player->is_digging = FALSE;
12666       player->is_collecting = FALSE;
12667       player->is_snapping = FALSE;
12668       player->is_pushing = FALSE;
12669     }
12670   }
12671
12672   if (!network.enabled && game.centered_player_nr == -1 &&
12673       !AllPlayersInSight(player, new_jx, new_jy))
12674     return MP_NO_ACTION;
12675
12676   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12677   if (can_move != MP_MOVING)
12678     return can_move;
12679
12680   // check if DigField() has caused relocation of the player
12681   if (player->jx != jx || player->jy != jy)
12682     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12683
12684   StorePlayer[jx][jy] = 0;
12685   player->last_jx = jx;
12686   player->last_jy = jy;
12687   player->jx = new_jx;
12688   player->jy = new_jy;
12689   StorePlayer[new_jx][new_jy] = player->element_nr;
12690
12691   if (player->move_delay_value_next != -1)
12692   {
12693     player->move_delay_value = player->move_delay_value_next;
12694     player->move_delay_value_next = -1;
12695   }
12696
12697   player->MovPos =
12698     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12699
12700   player->step_counter++;
12701
12702   PlayerVisit[jx][jy] = FrameCounter;
12703
12704   player->is_moving = TRUE;
12705
12706 #if 1
12707   // should better be called in MovePlayer(), but this breaks some tapes
12708   ScrollPlayer(player, SCROLL_INIT);
12709 #endif
12710
12711   return MP_MOVING;
12712 }
12713
12714 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12715 {
12716   int jx = player->jx, jy = player->jy;
12717   int old_jx = jx, old_jy = jy;
12718   int moved = MP_NO_ACTION;
12719
12720   if (!player->active)
12721     return FALSE;
12722
12723   if (!dx && !dy)
12724   {
12725     if (player->MovPos == 0)
12726     {
12727       player->is_moving = FALSE;
12728       player->is_digging = FALSE;
12729       player->is_collecting = FALSE;
12730       player->is_snapping = FALSE;
12731       player->is_pushing = FALSE;
12732     }
12733
12734     return FALSE;
12735   }
12736
12737   if (player->move_delay > 0)
12738     return FALSE;
12739
12740   player->move_delay = -1;              // set to "uninitialized" value
12741
12742   // store if player is automatically moved to next field
12743   player->is_auto_moving = (player->programmed_action != MV_NONE);
12744
12745   // remove the last programmed player action
12746   player->programmed_action = 0;
12747
12748   if (player->MovPos)
12749   {
12750     // should only happen if pre-1.2 tape recordings are played
12751     // this is only for backward compatibility
12752
12753     int original_move_delay_value = player->move_delay_value;
12754
12755 #if DEBUG
12756     Debug("game:playing:MovePlayer",
12757           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12758           tape.counter);
12759 #endif
12760
12761     // scroll remaining steps with finest movement resolution
12762     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12763
12764     while (player->MovPos)
12765     {
12766       ScrollPlayer(player, SCROLL_GO_ON);
12767       ScrollScreen(NULL, SCROLL_GO_ON);
12768
12769       AdvanceFrameAndPlayerCounters(player->index_nr);
12770
12771       DrawAllPlayers();
12772       BackToFront_WithFrameDelay(0);
12773     }
12774
12775     player->move_delay_value = original_move_delay_value;
12776   }
12777
12778   player->is_active = FALSE;
12779
12780   if (player->last_move_dir & MV_HORIZONTAL)
12781   {
12782     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12783       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12784   }
12785   else
12786   {
12787     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12788       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12789   }
12790
12791   if (!moved && !player->is_active)
12792   {
12793     player->is_moving = FALSE;
12794     player->is_digging = FALSE;
12795     player->is_collecting = FALSE;
12796     player->is_snapping = FALSE;
12797     player->is_pushing = FALSE;
12798   }
12799
12800   jx = player->jx;
12801   jy = player->jy;
12802
12803   if (moved & MP_MOVING && !ScreenMovPos &&
12804       (player->index_nr == game.centered_player_nr ||
12805        game.centered_player_nr == -1))
12806   {
12807     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12808
12809     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12810     {
12811       // actual player has left the screen -- scroll in that direction
12812       if (jx != old_jx)         // player has moved horizontally
12813         scroll_x += (jx - old_jx);
12814       else                      // player has moved vertically
12815         scroll_y += (jy - old_jy);
12816     }
12817     else
12818     {
12819       int offset_raw = game.scroll_delay_value;
12820
12821       if (jx != old_jx)         // player has moved horizontally
12822       {
12823         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12824         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12825         int new_scroll_x = jx - MIDPOSX + offset_x;
12826
12827         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12828             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12829           scroll_x = new_scroll_x;
12830
12831         // don't scroll over playfield boundaries
12832         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12833
12834         // don't scroll more than one field at a time
12835         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12836
12837         // don't scroll against the player's moving direction
12838         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12839             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12840           scroll_x = old_scroll_x;
12841       }
12842       else                      // player has moved vertically
12843       {
12844         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12845         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12846         int new_scroll_y = jy - MIDPOSY + offset_y;
12847
12848         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12849             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12850           scroll_y = new_scroll_y;
12851
12852         // don't scroll over playfield boundaries
12853         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12854
12855         // don't scroll more than one field at a time
12856         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12857
12858         // don't scroll against the player's moving direction
12859         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12860             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12861           scroll_y = old_scroll_y;
12862       }
12863     }
12864
12865     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12866     {
12867       if (!network.enabled && game.centered_player_nr == -1 &&
12868           !AllPlayersInVisibleScreen())
12869       {
12870         scroll_x = old_scroll_x;
12871         scroll_y = old_scroll_y;
12872       }
12873       else
12874       {
12875         ScrollScreen(player, SCROLL_INIT);
12876         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12877       }
12878     }
12879   }
12880
12881   player->StepFrame = 0;
12882
12883   if (moved & MP_MOVING)
12884   {
12885     if (old_jx != jx && old_jy == jy)
12886       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12887     else if (old_jx == jx && old_jy != jy)
12888       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12889
12890     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12891
12892     player->last_move_dir = player->MovDir;
12893     player->is_moving = TRUE;
12894     player->is_snapping = FALSE;
12895     player->is_switching = FALSE;
12896     player->is_dropping = FALSE;
12897     player->is_dropping_pressed = FALSE;
12898     player->drop_pressed_delay = 0;
12899
12900 #if 0
12901     // should better be called here than above, but this breaks some tapes
12902     ScrollPlayer(player, SCROLL_INIT);
12903 #endif
12904   }
12905   else
12906   {
12907     CheckGravityMovementWhenNotMoving(player);
12908
12909     player->is_moving = FALSE;
12910
12911     /* at this point, the player is allowed to move, but cannot move right now
12912        (e.g. because of something blocking the way) -- ensure that the player
12913        is also allowed to move in the next frame (in old versions before 3.1.1,
12914        the player was forced to wait again for eight frames before next try) */
12915
12916     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12917       player->move_delay = 0;   // allow direct movement in the next frame
12918   }
12919
12920   if (player->move_delay == -1)         // not yet initialized by DigField()
12921     player->move_delay = player->move_delay_value;
12922
12923   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12924   {
12925     TestIfPlayerTouchesBadThing(jx, jy);
12926     TestIfPlayerTouchesCustomElement(jx, jy);
12927   }
12928
12929   if (!player->active)
12930     RemovePlayer(player);
12931
12932   return moved;
12933 }
12934
12935 void ScrollPlayer(struct PlayerInfo *player, int mode)
12936 {
12937   int jx = player->jx, jy = player->jy;
12938   int last_jx = player->last_jx, last_jy = player->last_jy;
12939   int move_stepsize = TILEX / player->move_delay_value;
12940
12941   if (!player->active)
12942     return;
12943
12944   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12945     return;
12946
12947   if (mode == SCROLL_INIT)
12948   {
12949     player->actual_frame_counter = FrameCounter;
12950     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12951
12952     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12953         Tile[last_jx][last_jy] == EL_EMPTY)
12954     {
12955       int last_field_block_delay = 0;   // start with no blocking at all
12956       int block_delay_adjustment = player->block_delay_adjustment;
12957
12958       // if player blocks last field, add delay for exactly one move
12959       if (player->block_last_field)
12960       {
12961         last_field_block_delay += player->move_delay_value;
12962
12963         // when blocking enabled, prevent moving up despite gravity
12964         if (player->gravity && player->MovDir == MV_UP)
12965           block_delay_adjustment = -1;
12966       }
12967
12968       // add block delay adjustment (also possible when not blocking)
12969       last_field_block_delay += block_delay_adjustment;
12970
12971       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12972       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12973     }
12974
12975     if (player->MovPos != 0)    // player has not yet reached destination
12976       return;
12977   }
12978   else if (!FrameReached(&player->actual_frame_counter, 1))
12979     return;
12980
12981   if (player->MovPos != 0)
12982   {
12983     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12984     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12985
12986     // before DrawPlayer() to draw correct player graphic for this case
12987     if (player->MovPos == 0)
12988       CheckGravityMovement(player);
12989   }
12990
12991   if (player->MovPos == 0)      // player reached destination field
12992   {
12993     if (player->move_delay_reset_counter > 0)
12994     {
12995       player->move_delay_reset_counter--;
12996
12997       if (player->move_delay_reset_counter == 0)
12998       {
12999         // continue with normal speed after quickly moving through gate
13000         HALVE_PLAYER_SPEED(player);
13001
13002         // be able to make the next move without delay
13003         player->move_delay = 0;
13004       }
13005     }
13006
13007     player->last_jx = jx;
13008     player->last_jy = jy;
13009
13010     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13011         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13012         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13013         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13014         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13015         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13016         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13017         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13018     {
13019       ExitPlayer(player);
13020
13021       if (game.players_still_needed == 0 &&
13022           (game.friends_still_needed == 0 ||
13023            IS_SP_ELEMENT(Tile[jx][jy])))
13024         LevelSolved();
13025     }
13026
13027     // this breaks one level: "machine", level 000
13028     {
13029       int move_direction = player->MovDir;
13030       int enter_side = MV_DIR_OPPOSITE(move_direction);
13031       int leave_side = move_direction;
13032       int old_jx = last_jx;
13033       int old_jy = last_jy;
13034       int old_element = Tile[old_jx][old_jy];
13035       int new_element = Tile[jx][jy];
13036
13037       if (IS_CUSTOM_ELEMENT(old_element))
13038         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13039                                    CE_LEFT_BY_PLAYER,
13040                                    player->index_bit, leave_side);
13041
13042       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13043                                           CE_PLAYER_LEAVES_X,
13044                                           player->index_bit, leave_side);
13045
13046       if (IS_CUSTOM_ELEMENT(new_element))
13047         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13048                                    player->index_bit, enter_side);
13049
13050       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13051                                           CE_PLAYER_ENTERS_X,
13052                                           player->index_bit, enter_side);
13053
13054       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13055                                         CE_MOVE_OF_X, move_direction);
13056     }
13057
13058     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13059     {
13060       TestIfPlayerTouchesBadThing(jx, jy);
13061       TestIfPlayerTouchesCustomElement(jx, jy);
13062
13063       /* needed because pushed element has not yet reached its destination,
13064          so it would trigger a change event at its previous field location */
13065       if (!player->is_pushing)
13066         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13067
13068       if (level.finish_dig_collect &&
13069           (player->is_digging || player->is_collecting))
13070       {
13071         int last_element = player->last_removed_element;
13072         int move_direction = player->MovDir;
13073         int enter_side = MV_DIR_OPPOSITE(move_direction);
13074         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13075                             CE_PLAYER_COLLECTS_X);
13076
13077         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13078                                             player->index_bit, enter_side);
13079
13080         player->last_removed_element = EL_UNDEFINED;
13081       }
13082
13083       if (!player->active)
13084         RemovePlayer(player);
13085     }
13086
13087     if (!game.LevelSolved && level.use_step_counter)
13088     {
13089       int i;
13090
13091       TimePlayed++;
13092
13093       if (TimeLeft > 0)
13094       {
13095         TimeLeft--;
13096
13097         if (TimeLeft <= 10 && setup.time_limit)
13098           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13099
13100         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13101
13102         DisplayGameControlValues();
13103
13104         if (!TimeLeft && setup.time_limit)
13105           for (i = 0; i < MAX_PLAYERS; i++)
13106             KillPlayer(&stored_player[i]);
13107       }
13108       else if (game.no_time_limit && !game.all_players_gone)
13109       {
13110         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13111
13112         DisplayGameControlValues();
13113       }
13114     }
13115
13116     if (tape.single_step && tape.recording && !tape.pausing &&
13117         !player->programmed_action)
13118       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13119
13120     if (!player->programmed_action)
13121       CheckSaveEngineSnapshot(player);
13122   }
13123 }
13124
13125 void ScrollScreen(struct PlayerInfo *player, int mode)
13126 {
13127   static unsigned int screen_frame_counter = 0;
13128
13129   if (mode == SCROLL_INIT)
13130   {
13131     // set scrolling step size according to actual player's moving speed
13132     ScrollStepSize = TILEX / player->move_delay_value;
13133
13134     screen_frame_counter = FrameCounter;
13135     ScreenMovDir = player->MovDir;
13136     ScreenMovPos = player->MovPos;
13137     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13138     return;
13139   }
13140   else if (!FrameReached(&screen_frame_counter, 1))
13141     return;
13142
13143   if (ScreenMovPos)
13144   {
13145     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13146     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13147     redraw_mask |= REDRAW_FIELD;
13148   }
13149   else
13150     ScreenMovDir = MV_NONE;
13151 }
13152
13153 void TestIfPlayerTouchesCustomElement(int x, int y)
13154 {
13155   static int xy[4][2] =
13156   {
13157     { 0, -1 },
13158     { -1, 0 },
13159     { +1, 0 },
13160     { 0, +1 }
13161   };
13162   static int trigger_sides[4][2] =
13163   {
13164     // center side       border side
13165     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13166     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13167     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13168     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13169   };
13170   static int touch_dir[4] =
13171   {
13172     MV_LEFT | MV_RIGHT,
13173     MV_UP   | MV_DOWN,
13174     MV_UP   | MV_DOWN,
13175     MV_LEFT | MV_RIGHT
13176   };
13177   int center_element = Tile[x][y];      // should always be non-moving!
13178   int i;
13179
13180   for (i = 0; i < NUM_DIRECTIONS; i++)
13181   {
13182     int xx = x + xy[i][0];
13183     int yy = y + xy[i][1];
13184     int center_side = trigger_sides[i][0];
13185     int border_side = trigger_sides[i][1];
13186     int border_element;
13187
13188     if (!IN_LEV_FIELD(xx, yy))
13189       continue;
13190
13191     if (IS_PLAYER(x, y))                // player found at center element
13192     {
13193       struct PlayerInfo *player = PLAYERINFO(x, y);
13194
13195       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13196         border_element = Tile[xx][yy];          // may be moving!
13197       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13198         border_element = Tile[xx][yy];
13199       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13200         border_element = MovingOrBlocked2Element(xx, yy);
13201       else
13202         continue;               // center and border element do not touch
13203
13204       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13205                                  player->index_bit, border_side);
13206       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13207                                           CE_PLAYER_TOUCHES_X,
13208                                           player->index_bit, border_side);
13209
13210       {
13211         /* use player element that is initially defined in the level playfield,
13212            not the player element that corresponds to the runtime player number
13213            (example: a level that contains EL_PLAYER_3 as the only player would
13214            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13215         int player_element = PLAYERINFO(x, y)->initial_element;
13216
13217         CheckElementChangeBySide(xx, yy, border_element, player_element,
13218                                  CE_TOUCHING_X, border_side);
13219       }
13220     }
13221     else if (IS_PLAYER(xx, yy))         // player found at border element
13222     {
13223       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13224
13225       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13226       {
13227         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13228           continue;             // center and border element do not touch
13229       }
13230
13231       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13232                                  player->index_bit, center_side);
13233       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13234                                           CE_PLAYER_TOUCHES_X,
13235                                           player->index_bit, center_side);
13236
13237       {
13238         /* use player element that is initially defined in the level playfield,
13239            not the player element that corresponds to the runtime player number
13240            (example: a level that contains EL_PLAYER_3 as the only player would
13241            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13242         int player_element = PLAYERINFO(xx, yy)->initial_element;
13243
13244         CheckElementChangeBySide(x, y, center_element, player_element,
13245                                  CE_TOUCHING_X, center_side);
13246       }
13247
13248       break;
13249     }
13250   }
13251 }
13252
13253 void TestIfElementTouchesCustomElement(int x, int y)
13254 {
13255   static int xy[4][2] =
13256   {
13257     { 0, -1 },
13258     { -1, 0 },
13259     { +1, 0 },
13260     { 0, +1 }
13261   };
13262   static int trigger_sides[4][2] =
13263   {
13264     // center side      border side
13265     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13266     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13267     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13268     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13269   };
13270   static int touch_dir[4] =
13271   {
13272     MV_LEFT | MV_RIGHT,
13273     MV_UP   | MV_DOWN,
13274     MV_UP   | MV_DOWN,
13275     MV_LEFT | MV_RIGHT
13276   };
13277   boolean change_center_element = FALSE;
13278   int center_element = Tile[x][y];      // should always be non-moving!
13279   int border_element_old[NUM_DIRECTIONS];
13280   int i;
13281
13282   for (i = 0; i < NUM_DIRECTIONS; i++)
13283   {
13284     int xx = x + xy[i][0];
13285     int yy = y + xy[i][1];
13286     int border_element;
13287
13288     border_element_old[i] = -1;
13289
13290     if (!IN_LEV_FIELD(xx, yy))
13291       continue;
13292
13293     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13294       border_element = Tile[xx][yy];    // may be moving!
13295     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13296       border_element = Tile[xx][yy];
13297     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13298       border_element = MovingOrBlocked2Element(xx, yy);
13299     else
13300       continue;                 // center and border element do not touch
13301
13302     border_element_old[i] = border_element;
13303   }
13304
13305   for (i = 0; i < NUM_DIRECTIONS; i++)
13306   {
13307     int xx = x + xy[i][0];
13308     int yy = y + xy[i][1];
13309     int center_side = trigger_sides[i][0];
13310     int border_element = border_element_old[i];
13311
13312     if (border_element == -1)
13313       continue;
13314
13315     // check for change of border element
13316     CheckElementChangeBySide(xx, yy, border_element, center_element,
13317                              CE_TOUCHING_X, center_side);
13318
13319     // (center element cannot be player, so we dont have to check this here)
13320   }
13321
13322   for (i = 0; i < NUM_DIRECTIONS; i++)
13323   {
13324     int xx = x + xy[i][0];
13325     int yy = y + xy[i][1];
13326     int border_side = trigger_sides[i][1];
13327     int border_element = border_element_old[i];
13328
13329     if (border_element == -1)
13330       continue;
13331
13332     // check for change of center element (but change it only once)
13333     if (!change_center_element)
13334       change_center_element =
13335         CheckElementChangeBySide(x, y, center_element, border_element,
13336                                  CE_TOUCHING_X, border_side);
13337
13338     if (IS_PLAYER(xx, yy))
13339     {
13340       /* use player element that is initially defined in the level playfield,
13341          not the player element that corresponds to the runtime player number
13342          (example: a level that contains EL_PLAYER_3 as the only player would
13343          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13344       int player_element = PLAYERINFO(xx, yy)->initial_element;
13345
13346       CheckElementChangeBySide(x, y, center_element, player_element,
13347                                CE_TOUCHING_X, border_side);
13348     }
13349   }
13350 }
13351
13352 void TestIfElementHitsCustomElement(int x, int y, int direction)
13353 {
13354   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13355   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13356   int hitx = x + dx, hity = y + dy;
13357   int hitting_element = Tile[x][y];
13358   int touched_element;
13359
13360   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13361     return;
13362
13363   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13364                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13365
13366   if (IN_LEV_FIELD(hitx, hity))
13367   {
13368     int opposite_direction = MV_DIR_OPPOSITE(direction);
13369     int hitting_side = direction;
13370     int touched_side = opposite_direction;
13371     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13372                           MovDir[hitx][hity] != direction ||
13373                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13374
13375     object_hit = TRUE;
13376
13377     if (object_hit)
13378     {
13379       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13380                                CE_HITTING_X, touched_side);
13381
13382       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13383                                CE_HIT_BY_X, hitting_side);
13384
13385       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13386                                CE_HIT_BY_SOMETHING, opposite_direction);
13387
13388       if (IS_PLAYER(hitx, hity))
13389       {
13390         /* use player element that is initially defined in the level playfield,
13391            not the player element that corresponds to the runtime player number
13392            (example: a level that contains EL_PLAYER_3 as the only player would
13393            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13394         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13395
13396         CheckElementChangeBySide(x, y, hitting_element, player_element,
13397                                  CE_HITTING_X, touched_side);
13398       }
13399     }
13400   }
13401
13402   // "hitting something" is also true when hitting the playfield border
13403   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13404                            CE_HITTING_SOMETHING, direction);
13405 }
13406
13407 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13408 {
13409   int i, kill_x = -1, kill_y = -1;
13410
13411   int bad_element = -1;
13412   static int test_xy[4][2] =
13413   {
13414     { 0, -1 },
13415     { -1, 0 },
13416     { +1, 0 },
13417     { 0, +1 }
13418   };
13419   static int test_dir[4] =
13420   {
13421     MV_UP,
13422     MV_LEFT,
13423     MV_RIGHT,
13424     MV_DOWN
13425   };
13426
13427   for (i = 0; i < NUM_DIRECTIONS; i++)
13428   {
13429     int test_x, test_y, test_move_dir, test_element;
13430
13431     test_x = good_x + test_xy[i][0];
13432     test_y = good_y + test_xy[i][1];
13433
13434     if (!IN_LEV_FIELD(test_x, test_y))
13435       continue;
13436
13437     test_move_dir =
13438       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13439
13440     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13441
13442     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13443        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13444     */
13445     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13446         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13447     {
13448       kill_x = test_x;
13449       kill_y = test_y;
13450       bad_element = test_element;
13451
13452       break;
13453     }
13454   }
13455
13456   if (kill_x != -1 || kill_y != -1)
13457   {
13458     if (IS_PLAYER(good_x, good_y))
13459     {
13460       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13461
13462       if (player->shield_deadly_time_left > 0 &&
13463           !IS_INDESTRUCTIBLE(bad_element))
13464         Bang(kill_x, kill_y);
13465       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13466         KillPlayer(player);
13467     }
13468     else
13469       Bang(good_x, good_y);
13470   }
13471 }
13472
13473 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13474 {
13475   int i, kill_x = -1, kill_y = -1;
13476   int bad_element = Tile[bad_x][bad_y];
13477   static int test_xy[4][2] =
13478   {
13479     { 0, -1 },
13480     { -1, 0 },
13481     { +1, 0 },
13482     { 0, +1 }
13483   };
13484   static int touch_dir[4] =
13485   {
13486     MV_LEFT | MV_RIGHT,
13487     MV_UP   | MV_DOWN,
13488     MV_UP   | MV_DOWN,
13489     MV_LEFT | MV_RIGHT
13490   };
13491   static int test_dir[4] =
13492   {
13493     MV_UP,
13494     MV_LEFT,
13495     MV_RIGHT,
13496     MV_DOWN
13497   };
13498
13499   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13500     return;
13501
13502   for (i = 0; i < NUM_DIRECTIONS; i++)
13503   {
13504     int test_x, test_y, test_move_dir, test_element;
13505
13506     test_x = bad_x + test_xy[i][0];
13507     test_y = bad_y + test_xy[i][1];
13508
13509     if (!IN_LEV_FIELD(test_x, test_y))
13510       continue;
13511
13512     test_move_dir =
13513       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13514
13515     test_element = Tile[test_x][test_y];
13516
13517     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13518        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13519     */
13520     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13521         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13522     {
13523       // good thing is player or penguin that does not move away
13524       if (IS_PLAYER(test_x, test_y))
13525       {
13526         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13527
13528         if (bad_element == EL_ROBOT && player->is_moving)
13529           continue;     // robot does not kill player if he is moving
13530
13531         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13532         {
13533           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13534             continue;           // center and border element do not touch
13535         }
13536
13537         kill_x = test_x;
13538         kill_y = test_y;
13539
13540         break;
13541       }
13542       else if (test_element == EL_PENGUIN)
13543       {
13544         kill_x = test_x;
13545         kill_y = test_y;
13546
13547         break;
13548       }
13549     }
13550   }
13551
13552   if (kill_x != -1 || kill_y != -1)
13553   {
13554     if (IS_PLAYER(kill_x, kill_y))
13555     {
13556       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13557
13558       if (player->shield_deadly_time_left > 0 &&
13559           !IS_INDESTRUCTIBLE(bad_element))
13560         Bang(bad_x, bad_y);
13561       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13562         KillPlayer(player);
13563     }
13564     else
13565       Bang(kill_x, kill_y);
13566   }
13567 }
13568
13569 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13570 {
13571   int bad_element = Tile[bad_x][bad_y];
13572   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13573   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13574   int test_x = bad_x + dx, test_y = bad_y + dy;
13575   int test_move_dir, test_element;
13576   int kill_x = -1, kill_y = -1;
13577
13578   if (!IN_LEV_FIELD(test_x, test_y))
13579     return;
13580
13581   test_move_dir =
13582     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13583
13584   test_element = Tile[test_x][test_y];
13585
13586   if (test_move_dir != bad_move_dir)
13587   {
13588     // good thing can be player or penguin that does not move away
13589     if (IS_PLAYER(test_x, test_y))
13590     {
13591       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13592
13593       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13594          player as being hit when he is moving towards the bad thing, because
13595          the "get hit by" condition would be lost after the player stops) */
13596       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13597         return;         // player moves away from bad thing
13598
13599       kill_x = test_x;
13600       kill_y = test_y;
13601     }
13602     else if (test_element == EL_PENGUIN)
13603     {
13604       kill_x = test_x;
13605       kill_y = test_y;
13606     }
13607   }
13608
13609   if (kill_x != -1 || kill_y != -1)
13610   {
13611     if (IS_PLAYER(kill_x, kill_y))
13612     {
13613       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13614
13615       if (player->shield_deadly_time_left > 0 &&
13616           !IS_INDESTRUCTIBLE(bad_element))
13617         Bang(bad_x, bad_y);
13618       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13619         KillPlayer(player);
13620     }
13621     else
13622       Bang(kill_x, kill_y);
13623   }
13624 }
13625
13626 void TestIfPlayerTouchesBadThing(int x, int y)
13627 {
13628   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13629 }
13630
13631 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13632 {
13633   TestIfGoodThingHitsBadThing(x, y, move_dir);
13634 }
13635
13636 void TestIfBadThingTouchesPlayer(int x, int y)
13637 {
13638   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13639 }
13640
13641 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13642 {
13643   TestIfBadThingHitsGoodThing(x, y, move_dir);
13644 }
13645
13646 void TestIfFriendTouchesBadThing(int x, int y)
13647 {
13648   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13649 }
13650
13651 void TestIfBadThingTouchesFriend(int x, int y)
13652 {
13653   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13654 }
13655
13656 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13657 {
13658   int i, kill_x = bad_x, kill_y = bad_y;
13659   static int xy[4][2] =
13660   {
13661     { 0, -1 },
13662     { -1, 0 },
13663     { +1, 0 },
13664     { 0, +1 }
13665   };
13666
13667   for (i = 0; i < NUM_DIRECTIONS; i++)
13668   {
13669     int x, y, element;
13670
13671     x = bad_x + xy[i][0];
13672     y = bad_y + xy[i][1];
13673     if (!IN_LEV_FIELD(x, y))
13674       continue;
13675
13676     element = Tile[x][y];
13677     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13678         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13679     {
13680       kill_x = x;
13681       kill_y = y;
13682       break;
13683     }
13684   }
13685
13686   if (kill_x != bad_x || kill_y != bad_y)
13687     Bang(bad_x, bad_y);
13688 }
13689
13690 void KillPlayer(struct PlayerInfo *player)
13691 {
13692   int jx = player->jx, jy = player->jy;
13693
13694   if (!player->active)
13695     return;
13696
13697 #if 0
13698   Debug("game:playing:KillPlayer",
13699         "0: killed == %d, active == %d, reanimated == %d",
13700         player->killed, player->active, player->reanimated);
13701 #endif
13702
13703   /* the following code was introduced to prevent an infinite loop when calling
13704      -> Bang()
13705      -> CheckTriggeredElementChangeExt()
13706      -> ExecuteCustomElementAction()
13707      -> KillPlayer()
13708      -> (infinitely repeating the above sequence of function calls)
13709      which occurs when killing the player while having a CE with the setting
13710      "kill player X when explosion of <player X>"; the solution using a new
13711      field "player->killed" was chosen for backwards compatibility, although
13712      clever use of the fields "player->active" etc. would probably also work */
13713 #if 1
13714   if (player->killed)
13715     return;
13716 #endif
13717
13718   player->killed = TRUE;
13719
13720   // remove accessible field at the player's position
13721   Tile[jx][jy] = EL_EMPTY;
13722
13723   // deactivate shield (else Bang()/Explode() would not work right)
13724   player->shield_normal_time_left = 0;
13725   player->shield_deadly_time_left = 0;
13726
13727 #if 0
13728   Debug("game:playing:KillPlayer",
13729         "1: killed == %d, active == %d, reanimated == %d",
13730         player->killed, player->active, player->reanimated);
13731 #endif
13732
13733   Bang(jx, jy);
13734
13735 #if 0
13736   Debug("game:playing:KillPlayer",
13737         "2: killed == %d, active == %d, reanimated == %d",
13738         player->killed, player->active, player->reanimated);
13739 #endif
13740
13741   if (player->reanimated)       // killed player may have been reanimated
13742     player->killed = player->reanimated = FALSE;
13743   else
13744     BuryPlayer(player);
13745 }
13746
13747 static void KillPlayerUnlessEnemyProtected(int x, int y)
13748 {
13749   if (!PLAYER_ENEMY_PROTECTED(x, y))
13750     KillPlayer(PLAYERINFO(x, y));
13751 }
13752
13753 static void KillPlayerUnlessExplosionProtected(int x, int y)
13754 {
13755   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13756     KillPlayer(PLAYERINFO(x, y));
13757 }
13758
13759 void BuryPlayer(struct PlayerInfo *player)
13760 {
13761   int jx = player->jx, jy = player->jy;
13762
13763   if (!player->active)
13764     return;
13765
13766   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13767   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13768
13769   RemovePlayer(player);
13770
13771   player->buried = TRUE;
13772
13773   if (game.all_players_gone)
13774     game.GameOver = TRUE;
13775 }
13776
13777 void RemovePlayer(struct PlayerInfo *player)
13778 {
13779   int jx = player->jx, jy = player->jy;
13780   int i, found = FALSE;
13781
13782   player->present = FALSE;
13783   player->active = FALSE;
13784
13785   // required for some CE actions (even if the player is not active anymore)
13786   player->MovPos = 0;
13787
13788   if (!ExplodeField[jx][jy])
13789     StorePlayer[jx][jy] = 0;
13790
13791   if (player->is_moving)
13792     TEST_DrawLevelField(player->last_jx, player->last_jy);
13793
13794   for (i = 0; i < MAX_PLAYERS; i++)
13795     if (stored_player[i].active)
13796       found = TRUE;
13797
13798   if (!found)
13799   {
13800     game.all_players_gone = TRUE;
13801     game.GameOver = TRUE;
13802   }
13803
13804   game.exit_x = game.robot_wheel_x = jx;
13805   game.exit_y = game.robot_wheel_y = jy;
13806 }
13807
13808 void ExitPlayer(struct PlayerInfo *player)
13809 {
13810   DrawPlayer(player);   // needed here only to cleanup last field
13811   RemovePlayer(player);
13812
13813   if (game.players_still_needed > 0)
13814     game.players_still_needed--;
13815 }
13816
13817 static void SetFieldForSnapping(int x, int y, int element, int direction,
13818                                 int player_index_bit)
13819 {
13820   struct ElementInfo *ei = &element_info[element];
13821   int direction_bit = MV_DIR_TO_BIT(direction);
13822   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13823   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13824                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13825
13826   Tile[x][y] = EL_ELEMENT_SNAPPING;
13827   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13828   MovDir[x][y] = direction;
13829   Store[x][y] = element;
13830   Store2[x][y] = player_index_bit;
13831
13832   ResetGfxAnimation(x, y);
13833
13834   GfxElement[x][y] = element;
13835   GfxAction[x][y] = action;
13836   GfxDir[x][y] = direction;
13837   GfxFrame[x][y] = -1;
13838 }
13839
13840 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13841                                    int player_index_bit)
13842 {
13843   TestIfElementTouchesCustomElement(x, y);      // for empty space
13844
13845   if (level.finish_dig_collect)
13846   {
13847     int dig_side = MV_DIR_OPPOSITE(direction);
13848
13849     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13850                                         player_index_bit, dig_side);
13851   }
13852 }
13853
13854 /*
13855   =============================================================================
13856   checkDiagonalPushing()
13857   -----------------------------------------------------------------------------
13858   check if diagonal input device direction results in pushing of object
13859   (by checking if the alternative direction is walkable, diggable, ...)
13860   =============================================================================
13861 */
13862
13863 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13864                                     int x, int y, int real_dx, int real_dy)
13865 {
13866   int jx, jy, dx, dy, xx, yy;
13867
13868   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13869     return TRUE;
13870
13871   // diagonal direction: check alternative direction
13872   jx = player->jx;
13873   jy = player->jy;
13874   dx = x - jx;
13875   dy = y - jy;
13876   xx = jx + (dx == 0 ? real_dx : 0);
13877   yy = jy + (dy == 0 ? real_dy : 0);
13878
13879   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13880 }
13881
13882 /*
13883   =============================================================================
13884   DigField()
13885   -----------------------------------------------------------------------------
13886   x, y:                 field next to player (non-diagonal) to try to dig to
13887   real_dx, real_dy:     direction as read from input device (can be diagonal)
13888   =============================================================================
13889 */
13890
13891 static int DigField(struct PlayerInfo *player,
13892                     int oldx, int oldy, int x, int y,
13893                     int real_dx, int real_dy, int mode)
13894 {
13895   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13896   boolean player_was_pushing = player->is_pushing;
13897   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13898   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13899   int jx = oldx, jy = oldy;
13900   int dx = x - jx, dy = y - jy;
13901   int nextx = x + dx, nexty = y + dy;
13902   int move_direction = (dx == -1 ? MV_LEFT  :
13903                         dx == +1 ? MV_RIGHT :
13904                         dy == -1 ? MV_UP    :
13905                         dy == +1 ? MV_DOWN  : MV_NONE);
13906   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13907   int dig_side = MV_DIR_OPPOSITE(move_direction);
13908   int old_element = Tile[jx][jy];
13909   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13910   int collect_count;
13911
13912   if (is_player)                // function can also be called by EL_PENGUIN
13913   {
13914     if (player->MovPos == 0)
13915     {
13916       player->is_digging = FALSE;
13917       player->is_collecting = FALSE;
13918     }
13919
13920     if (player->MovPos == 0)    // last pushing move finished
13921       player->is_pushing = FALSE;
13922
13923     if (mode == DF_NO_PUSH)     // player just stopped pushing
13924     {
13925       player->is_switching = FALSE;
13926       player->push_delay = -1;
13927
13928       return MP_NO_ACTION;
13929     }
13930   }
13931
13932   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13933     old_element = Back[jx][jy];
13934
13935   // in case of element dropped at player position, check background
13936   else if (Back[jx][jy] != EL_EMPTY &&
13937            game.engine_version >= VERSION_IDENT(2,2,0,0))
13938     old_element = Back[jx][jy];
13939
13940   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13941     return MP_NO_ACTION;        // field has no opening in this direction
13942
13943   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13944     return MP_NO_ACTION;        // field has no opening in this direction
13945
13946   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13947   {
13948     SplashAcid(x, y);
13949
13950     Tile[jx][jy] = player->artwork_element;
13951     InitMovingField(jx, jy, MV_DOWN);
13952     Store[jx][jy] = EL_ACID;
13953     ContinueMoving(jx, jy);
13954     BuryPlayer(player);
13955
13956     return MP_DONT_RUN_INTO;
13957   }
13958
13959   if (player_can_move && DONT_RUN_INTO(element))
13960   {
13961     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13962
13963     return MP_DONT_RUN_INTO;
13964   }
13965
13966   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13967     return MP_NO_ACTION;
13968
13969   collect_count = element_info[element].collect_count_initial;
13970
13971   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13972     return MP_NO_ACTION;
13973
13974   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13975     player_can_move = player_can_move_or_snap;
13976
13977   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13978       game.engine_version >= VERSION_IDENT(2,2,0,0))
13979   {
13980     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13981                                player->index_bit, dig_side);
13982     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13983                                         player->index_bit, dig_side);
13984
13985     if (element == EL_DC_LANDMINE)
13986       Bang(x, y);
13987
13988     if (Tile[x][y] != element)          // field changed by snapping
13989       return MP_ACTION;
13990
13991     return MP_NO_ACTION;
13992   }
13993
13994   if (player->gravity && is_player && !player->is_auto_moving &&
13995       canFallDown(player) && move_direction != MV_DOWN &&
13996       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13997     return MP_NO_ACTION;        // player cannot walk here due to gravity
13998
13999   if (player_can_move &&
14000       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14001   {
14002     int sound_element = SND_ELEMENT(element);
14003     int sound_action = ACTION_WALKING;
14004
14005     if (IS_RND_GATE(element))
14006     {
14007       if (!player->key[RND_GATE_NR(element)])
14008         return MP_NO_ACTION;
14009     }
14010     else if (IS_RND_GATE_GRAY(element))
14011     {
14012       if (!player->key[RND_GATE_GRAY_NR(element)])
14013         return MP_NO_ACTION;
14014     }
14015     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14016     {
14017       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14018         return MP_NO_ACTION;
14019     }
14020     else if (element == EL_EXIT_OPEN ||
14021              element == EL_EM_EXIT_OPEN ||
14022              element == EL_EM_EXIT_OPENING ||
14023              element == EL_STEEL_EXIT_OPEN ||
14024              element == EL_EM_STEEL_EXIT_OPEN ||
14025              element == EL_EM_STEEL_EXIT_OPENING ||
14026              element == EL_SP_EXIT_OPEN ||
14027              element == EL_SP_EXIT_OPENING)
14028     {
14029       sound_action = ACTION_PASSING;    // player is passing exit
14030     }
14031     else if (element == EL_EMPTY)
14032     {
14033       sound_action = ACTION_MOVING;             // nothing to walk on
14034     }
14035
14036     // play sound from background or player, whatever is available
14037     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14038       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14039     else
14040       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14041   }
14042   else if (player_can_move &&
14043            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14044   {
14045     if (!ACCESS_FROM(element, opposite_direction))
14046       return MP_NO_ACTION;      // field not accessible from this direction
14047
14048     if (CAN_MOVE(element))      // only fixed elements can be passed!
14049       return MP_NO_ACTION;
14050
14051     if (IS_EM_GATE(element))
14052     {
14053       if (!player->key[EM_GATE_NR(element)])
14054         return MP_NO_ACTION;
14055     }
14056     else if (IS_EM_GATE_GRAY(element))
14057     {
14058       if (!player->key[EM_GATE_GRAY_NR(element)])
14059         return MP_NO_ACTION;
14060     }
14061     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14062     {
14063       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14064         return MP_NO_ACTION;
14065     }
14066     else if (IS_EMC_GATE(element))
14067     {
14068       if (!player->key[EMC_GATE_NR(element)])
14069         return MP_NO_ACTION;
14070     }
14071     else if (IS_EMC_GATE_GRAY(element))
14072     {
14073       if (!player->key[EMC_GATE_GRAY_NR(element)])
14074         return MP_NO_ACTION;
14075     }
14076     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14077     {
14078       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14079         return MP_NO_ACTION;
14080     }
14081     else if (element == EL_DC_GATE_WHITE ||
14082              element == EL_DC_GATE_WHITE_GRAY ||
14083              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14084     {
14085       if (player->num_white_keys == 0)
14086         return MP_NO_ACTION;
14087
14088       player->num_white_keys--;
14089     }
14090     else if (IS_SP_PORT(element))
14091     {
14092       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14093           element == EL_SP_GRAVITY_PORT_RIGHT ||
14094           element == EL_SP_GRAVITY_PORT_UP ||
14095           element == EL_SP_GRAVITY_PORT_DOWN)
14096         player->gravity = !player->gravity;
14097       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14098                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14099                element == EL_SP_GRAVITY_ON_PORT_UP ||
14100                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14101         player->gravity = TRUE;
14102       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14103                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14104                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14105                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14106         player->gravity = FALSE;
14107     }
14108
14109     // automatically move to the next field with double speed
14110     player->programmed_action = move_direction;
14111
14112     if (player->move_delay_reset_counter == 0)
14113     {
14114       player->move_delay_reset_counter = 2;     // two double speed steps
14115
14116       DOUBLE_PLAYER_SPEED(player);
14117     }
14118
14119     PlayLevelSoundAction(x, y, ACTION_PASSING);
14120   }
14121   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14122   {
14123     RemoveField(x, y);
14124
14125     if (mode != DF_SNAP)
14126     {
14127       GfxElement[x][y] = GFX_ELEMENT(element);
14128       player->is_digging = TRUE;
14129     }
14130
14131     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14132
14133     // use old behaviour for old levels (digging)
14134     if (!level.finish_dig_collect)
14135     {
14136       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14137                                           player->index_bit, dig_side);
14138
14139       // if digging triggered player relocation, finish digging tile
14140       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14141         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14142     }
14143
14144     if (mode == DF_SNAP)
14145     {
14146       if (level.block_snap_field)
14147         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14148       else
14149         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14150
14151       // use old behaviour for old levels (snapping)
14152       if (!level.finish_dig_collect)
14153         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14154                                             player->index_bit, dig_side);
14155     }
14156   }
14157   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14158   {
14159     RemoveField(x, y);
14160
14161     if (is_player && mode != DF_SNAP)
14162     {
14163       GfxElement[x][y] = element;
14164       player->is_collecting = TRUE;
14165     }
14166
14167     if (element == EL_SPEED_PILL)
14168     {
14169       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14170     }
14171     else if (element == EL_EXTRA_TIME && level.time > 0)
14172     {
14173       TimeLeft += level.extra_time;
14174
14175       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14176
14177       DisplayGameControlValues();
14178     }
14179     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14180     {
14181       player->shield_normal_time_left += level.shield_normal_time;
14182       if (element == EL_SHIELD_DEADLY)
14183         player->shield_deadly_time_left += level.shield_deadly_time;
14184     }
14185     else if (element == EL_DYNAMITE ||
14186              element == EL_EM_DYNAMITE ||
14187              element == EL_SP_DISK_RED)
14188     {
14189       if (player->inventory_size < MAX_INVENTORY_SIZE)
14190         player->inventory_element[player->inventory_size++] = element;
14191
14192       DrawGameDoorValues();
14193     }
14194     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14195     {
14196       player->dynabomb_count++;
14197       player->dynabombs_left++;
14198     }
14199     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14200     {
14201       player->dynabomb_size++;
14202     }
14203     else if (element == EL_DYNABOMB_INCREASE_POWER)
14204     {
14205       player->dynabomb_xl = TRUE;
14206     }
14207     else if (IS_KEY(element))
14208     {
14209       player->key[KEY_NR(element)] = TRUE;
14210
14211       DrawGameDoorValues();
14212     }
14213     else if (element == EL_DC_KEY_WHITE)
14214     {
14215       player->num_white_keys++;
14216
14217       // display white keys?
14218       // DrawGameDoorValues();
14219     }
14220     else if (IS_ENVELOPE(element))
14221     {
14222       player->show_envelope = element;
14223     }
14224     else if (element == EL_EMC_LENSES)
14225     {
14226       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14227
14228       RedrawAllInvisibleElementsForLenses();
14229     }
14230     else if (element == EL_EMC_MAGNIFIER)
14231     {
14232       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14233
14234       RedrawAllInvisibleElementsForMagnifier();
14235     }
14236     else if (IS_DROPPABLE(element) ||
14237              IS_THROWABLE(element))     // can be collected and dropped
14238     {
14239       int i;
14240
14241       if (collect_count == 0)
14242         player->inventory_infinite_element = element;
14243       else
14244         for (i = 0; i < collect_count; i++)
14245           if (player->inventory_size < MAX_INVENTORY_SIZE)
14246             player->inventory_element[player->inventory_size++] = element;
14247
14248       DrawGameDoorValues();
14249     }
14250     else if (collect_count > 0)
14251     {
14252       game.gems_still_needed -= collect_count;
14253       if (game.gems_still_needed < 0)
14254         game.gems_still_needed = 0;
14255
14256       game.snapshot.collected_item = TRUE;
14257
14258       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14259
14260       DisplayGameControlValues();
14261     }
14262
14263     RaiseScoreElement(element);
14264     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14265
14266     // use old behaviour for old levels (collecting)
14267     if (!level.finish_dig_collect && is_player)
14268     {
14269       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14270                                           player->index_bit, dig_side);
14271
14272       // if collecting triggered player relocation, finish collecting tile
14273       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14274         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14275     }
14276
14277     if (mode == DF_SNAP)
14278     {
14279       if (level.block_snap_field)
14280         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14281       else
14282         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14283
14284       // use old behaviour for old levels (snapping)
14285       if (!level.finish_dig_collect)
14286         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14287                                             player->index_bit, dig_side);
14288     }
14289   }
14290   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14291   {
14292     if (mode == DF_SNAP && element != EL_BD_ROCK)
14293       return MP_NO_ACTION;
14294
14295     if (CAN_FALL(element) && dy)
14296       return MP_NO_ACTION;
14297
14298     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14299         !(element == EL_SPRING && level.use_spring_bug))
14300       return MP_NO_ACTION;
14301
14302     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14303         ((move_direction & MV_VERTICAL &&
14304           ((element_info[element].move_pattern & MV_LEFT &&
14305             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14306            (element_info[element].move_pattern & MV_RIGHT &&
14307             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14308          (move_direction & MV_HORIZONTAL &&
14309           ((element_info[element].move_pattern & MV_UP &&
14310             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14311            (element_info[element].move_pattern & MV_DOWN &&
14312             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14313       return MP_NO_ACTION;
14314
14315     // do not push elements already moving away faster than player
14316     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14317         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14318       return MP_NO_ACTION;
14319
14320     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14321     {
14322       if (player->push_delay_value == -1 || !player_was_pushing)
14323         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14324     }
14325     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14326     {
14327       if (player->push_delay_value == -1)
14328         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14329     }
14330     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14331     {
14332       if (!player->is_pushing)
14333         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14334     }
14335
14336     player->is_pushing = TRUE;
14337     player->is_active = TRUE;
14338
14339     if (!(IN_LEV_FIELD(nextx, nexty) &&
14340           (IS_FREE(nextx, nexty) ||
14341            (IS_SB_ELEMENT(element) &&
14342             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14343            (IS_CUSTOM_ELEMENT(element) &&
14344             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14345       return MP_NO_ACTION;
14346
14347     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14348       return MP_NO_ACTION;
14349
14350     if (player->push_delay == -1)       // new pushing; restart delay
14351       player->push_delay = 0;
14352
14353     if (player->push_delay < player->push_delay_value &&
14354         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14355         element != EL_SPRING && element != EL_BALLOON)
14356     {
14357       // make sure that there is no move delay before next try to push
14358       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14359         player->move_delay = 0;
14360
14361       return MP_NO_ACTION;
14362     }
14363
14364     if (IS_CUSTOM_ELEMENT(element) &&
14365         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14366     {
14367       if (!DigFieldByCE(nextx, nexty, element))
14368         return MP_NO_ACTION;
14369     }
14370
14371     if (IS_SB_ELEMENT(element))
14372     {
14373       boolean sokoban_task_solved = FALSE;
14374
14375       if (element == EL_SOKOBAN_FIELD_FULL)
14376       {
14377         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14378
14379         IncrementSokobanFieldsNeeded();
14380         IncrementSokobanObjectsNeeded();
14381       }
14382
14383       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14384       {
14385         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14386
14387         DecrementSokobanFieldsNeeded();
14388         DecrementSokobanObjectsNeeded();
14389
14390         // sokoban object was pushed from empty field to sokoban field
14391         if (Back[x][y] == EL_EMPTY)
14392           sokoban_task_solved = TRUE;
14393       }
14394
14395       Tile[x][y] = EL_SOKOBAN_OBJECT;
14396
14397       if (Back[x][y] == Back[nextx][nexty])
14398         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14399       else if (Back[x][y] != 0)
14400         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14401                                     ACTION_EMPTYING);
14402       else
14403         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14404                                     ACTION_FILLING);
14405
14406       if (sokoban_task_solved &&
14407           game.sokoban_fields_still_needed == 0 &&
14408           game.sokoban_objects_still_needed == 0 &&
14409           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14410       {
14411         game.players_still_needed = 0;
14412
14413         LevelSolved();
14414
14415         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14416       }
14417     }
14418     else
14419       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14420
14421     InitMovingField(x, y, move_direction);
14422     GfxAction[x][y] = ACTION_PUSHING;
14423
14424     if (mode == DF_SNAP)
14425       ContinueMoving(x, y);
14426     else
14427       MovPos[x][y] = (dx != 0 ? dx : dy);
14428
14429     Pushed[x][y] = TRUE;
14430     Pushed[nextx][nexty] = TRUE;
14431
14432     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14433       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14434     else
14435       player->push_delay_value = -1;    // get new value later
14436
14437     // check for element change _after_ element has been pushed
14438     if (game.use_change_when_pushing_bug)
14439     {
14440       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14441                                  player->index_bit, dig_side);
14442       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14443                                           player->index_bit, dig_side);
14444     }
14445   }
14446   else if (IS_SWITCHABLE(element))
14447   {
14448     if (PLAYER_SWITCHING(player, x, y))
14449     {
14450       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14451                                           player->index_bit, dig_side);
14452
14453       return MP_ACTION;
14454     }
14455
14456     player->is_switching = TRUE;
14457     player->switch_x = x;
14458     player->switch_y = y;
14459
14460     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14461
14462     if (element == EL_ROBOT_WHEEL)
14463     {
14464       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14465
14466       game.robot_wheel_x = x;
14467       game.robot_wheel_y = y;
14468       game.robot_wheel_active = TRUE;
14469
14470       TEST_DrawLevelField(x, y);
14471     }
14472     else if (element == EL_SP_TERMINAL)
14473     {
14474       int xx, yy;
14475
14476       SCAN_PLAYFIELD(xx, yy)
14477       {
14478         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14479         {
14480           Bang(xx, yy);
14481         }
14482         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14483         {
14484           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14485
14486           ResetGfxAnimation(xx, yy);
14487           TEST_DrawLevelField(xx, yy);
14488         }
14489       }
14490     }
14491     else if (IS_BELT_SWITCH(element))
14492     {
14493       ToggleBeltSwitch(x, y);
14494     }
14495     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14496              element == EL_SWITCHGATE_SWITCH_DOWN ||
14497              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14498              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14499     {
14500       ToggleSwitchgateSwitch(x, y);
14501     }
14502     else if (element == EL_LIGHT_SWITCH ||
14503              element == EL_LIGHT_SWITCH_ACTIVE)
14504     {
14505       ToggleLightSwitch(x, y);
14506     }
14507     else if (element == EL_TIMEGATE_SWITCH ||
14508              element == EL_DC_TIMEGATE_SWITCH)
14509     {
14510       ActivateTimegateSwitch(x, y);
14511     }
14512     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14513              element == EL_BALLOON_SWITCH_RIGHT ||
14514              element == EL_BALLOON_SWITCH_UP    ||
14515              element == EL_BALLOON_SWITCH_DOWN  ||
14516              element == EL_BALLOON_SWITCH_NONE  ||
14517              element == EL_BALLOON_SWITCH_ANY)
14518     {
14519       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14520                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14521                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14522                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14523                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14524                              move_direction);
14525     }
14526     else if (element == EL_LAMP)
14527     {
14528       Tile[x][y] = EL_LAMP_ACTIVE;
14529       game.lights_still_needed--;
14530
14531       ResetGfxAnimation(x, y);
14532       TEST_DrawLevelField(x, y);
14533     }
14534     else if (element == EL_TIME_ORB_FULL)
14535     {
14536       Tile[x][y] = EL_TIME_ORB_EMPTY;
14537
14538       if (level.time > 0 || level.use_time_orb_bug)
14539       {
14540         TimeLeft += level.time_orb_time;
14541         game.no_time_limit = FALSE;
14542
14543         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14544
14545         DisplayGameControlValues();
14546       }
14547
14548       ResetGfxAnimation(x, y);
14549       TEST_DrawLevelField(x, y);
14550     }
14551     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14552              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14553     {
14554       int xx, yy;
14555
14556       game.ball_active = !game.ball_active;
14557
14558       SCAN_PLAYFIELD(xx, yy)
14559       {
14560         int e = Tile[xx][yy];
14561
14562         if (game.ball_active)
14563         {
14564           if (e == EL_EMC_MAGIC_BALL)
14565             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14566           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14567             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14568         }
14569         else
14570         {
14571           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14572             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14573           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14574             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14575         }
14576       }
14577     }
14578
14579     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14580                                         player->index_bit, dig_side);
14581
14582     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14583                                         player->index_bit, dig_side);
14584
14585     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14586                                         player->index_bit, dig_side);
14587
14588     return MP_ACTION;
14589   }
14590   else
14591   {
14592     if (!PLAYER_SWITCHING(player, x, y))
14593     {
14594       player->is_switching = TRUE;
14595       player->switch_x = x;
14596       player->switch_y = y;
14597
14598       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14599                                  player->index_bit, dig_side);
14600       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14601                                           player->index_bit, dig_side);
14602
14603       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14604                                  player->index_bit, dig_side);
14605       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14606                                           player->index_bit, dig_side);
14607     }
14608
14609     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14610                                player->index_bit, dig_side);
14611     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14612                                         player->index_bit, dig_side);
14613
14614     return MP_NO_ACTION;
14615   }
14616
14617   player->push_delay = -1;
14618
14619   if (is_player)                // function can also be called by EL_PENGUIN
14620   {
14621     if (Tile[x][y] != element)          // really digged/collected something
14622     {
14623       player->is_collecting = !player->is_digging;
14624       player->is_active = TRUE;
14625
14626       player->last_removed_element = element;
14627     }
14628   }
14629
14630   return MP_MOVING;
14631 }
14632
14633 static boolean DigFieldByCE(int x, int y, int digging_element)
14634 {
14635   int element = Tile[x][y];
14636
14637   if (!IS_FREE(x, y))
14638   {
14639     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14640                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14641                   ACTION_BREAKING);
14642
14643     // no element can dig solid indestructible elements
14644     if (IS_INDESTRUCTIBLE(element) &&
14645         !IS_DIGGABLE(element) &&
14646         !IS_COLLECTIBLE(element))
14647       return FALSE;
14648
14649     if (AmoebaNr[x][y] &&
14650         (element == EL_AMOEBA_FULL ||
14651          element == EL_BD_AMOEBA ||
14652          element == EL_AMOEBA_GROWING))
14653     {
14654       AmoebaCnt[AmoebaNr[x][y]]--;
14655       AmoebaCnt2[AmoebaNr[x][y]]--;
14656     }
14657
14658     if (IS_MOVING(x, y))
14659       RemoveMovingField(x, y);
14660     else
14661     {
14662       RemoveField(x, y);
14663       TEST_DrawLevelField(x, y);
14664     }
14665
14666     // if digged element was about to explode, prevent the explosion
14667     ExplodeField[x][y] = EX_TYPE_NONE;
14668
14669     PlayLevelSoundAction(x, y, action);
14670   }
14671
14672   Store[x][y] = EL_EMPTY;
14673
14674   // this makes it possible to leave the removed element again
14675   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14676     Store[x][y] = element;
14677
14678   return TRUE;
14679 }
14680
14681 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14682 {
14683   int jx = player->jx, jy = player->jy;
14684   int x = jx + dx, y = jy + dy;
14685   int snap_direction = (dx == -1 ? MV_LEFT  :
14686                         dx == +1 ? MV_RIGHT :
14687                         dy == -1 ? MV_UP    :
14688                         dy == +1 ? MV_DOWN  : MV_NONE);
14689   boolean can_continue_snapping = (level.continuous_snapping &&
14690                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14691
14692   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14693     return FALSE;
14694
14695   if (!player->active || !IN_LEV_FIELD(x, y))
14696     return FALSE;
14697
14698   if (dx && dy)
14699     return FALSE;
14700
14701   if (!dx && !dy)
14702   {
14703     if (player->MovPos == 0)
14704       player->is_pushing = FALSE;
14705
14706     player->is_snapping = FALSE;
14707
14708     if (player->MovPos == 0)
14709     {
14710       player->is_moving = FALSE;
14711       player->is_digging = FALSE;
14712       player->is_collecting = FALSE;
14713     }
14714
14715     return FALSE;
14716   }
14717
14718   // prevent snapping with already pressed snap key when not allowed
14719   if (player->is_snapping && !can_continue_snapping)
14720     return FALSE;
14721
14722   player->MovDir = snap_direction;
14723
14724   if (player->MovPos == 0)
14725   {
14726     player->is_moving = FALSE;
14727     player->is_digging = FALSE;
14728     player->is_collecting = FALSE;
14729   }
14730
14731   player->is_dropping = FALSE;
14732   player->is_dropping_pressed = FALSE;
14733   player->drop_pressed_delay = 0;
14734
14735   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14736     return FALSE;
14737
14738   player->is_snapping = TRUE;
14739   player->is_active = TRUE;
14740
14741   if (player->MovPos == 0)
14742   {
14743     player->is_moving = FALSE;
14744     player->is_digging = FALSE;
14745     player->is_collecting = FALSE;
14746   }
14747
14748   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14749     TEST_DrawLevelField(player->last_jx, player->last_jy);
14750
14751   TEST_DrawLevelField(x, y);
14752
14753   return TRUE;
14754 }
14755
14756 static boolean DropElement(struct PlayerInfo *player)
14757 {
14758   int old_element, new_element;
14759   int dropx = player->jx, dropy = player->jy;
14760   int drop_direction = player->MovDir;
14761   int drop_side = drop_direction;
14762   int drop_element = get_next_dropped_element(player);
14763
14764   /* do not drop an element on top of another element; when holding drop key
14765      pressed without moving, dropped element must move away before the next
14766      element can be dropped (this is especially important if the next element
14767      is dynamite, which can be placed on background for historical reasons) */
14768   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14769     return MP_ACTION;
14770
14771   if (IS_THROWABLE(drop_element))
14772   {
14773     dropx += GET_DX_FROM_DIR(drop_direction);
14774     dropy += GET_DY_FROM_DIR(drop_direction);
14775
14776     if (!IN_LEV_FIELD(dropx, dropy))
14777       return FALSE;
14778   }
14779
14780   old_element = Tile[dropx][dropy];     // old element at dropping position
14781   new_element = drop_element;           // default: no change when dropping
14782
14783   // check if player is active, not moving and ready to drop
14784   if (!player->active || player->MovPos || player->drop_delay > 0)
14785     return FALSE;
14786
14787   // check if player has anything that can be dropped
14788   if (new_element == EL_UNDEFINED)
14789     return FALSE;
14790
14791   // only set if player has anything that can be dropped
14792   player->is_dropping_pressed = TRUE;
14793
14794   // check if drop key was pressed long enough for EM style dynamite
14795   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14796     return FALSE;
14797
14798   // check if anything can be dropped at the current position
14799   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14800     return FALSE;
14801
14802   // collected custom elements can only be dropped on empty fields
14803   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14804     return FALSE;
14805
14806   if (old_element != EL_EMPTY)
14807     Back[dropx][dropy] = old_element;   // store old element on this field
14808
14809   ResetGfxAnimation(dropx, dropy);
14810   ResetRandomAnimationValue(dropx, dropy);
14811
14812   if (player->inventory_size > 0 ||
14813       player->inventory_infinite_element != EL_UNDEFINED)
14814   {
14815     if (player->inventory_size > 0)
14816     {
14817       player->inventory_size--;
14818
14819       DrawGameDoorValues();
14820
14821       if (new_element == EL_DYNAMITE)
14822         new_element = EL_DYNAMITE_ACTIVE;
14823       else if (new_element == EL_EM_DYNAMITE)
14824         new_element = EL_EM_DYNAMITE_ACTIVE;
14825       else if (new_element == EL_SP_DISK_RED)
14826         new_element = EL_SP_DISK_RED_ACTIVE;
14827     }
14828
14829     Tile[dropx][dropy] = new_element;
14830
14831     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14832       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14833                           el2img(Tile[dropx][dropy]), 0);
14834
14835     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14836
14837     // needed if previous element just changed to "empty" in the last frame
14838     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14839
14840     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14841                                player->index_bit, drop_side);
14842     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14843                                         CE_PLAYER_DROPS_X,
14844                                         player->index_bit, drop_side);
14845
14846     TestIfElementTouchesCustomElement(dropx, dropy);
14847   }
14848   else          // player is dropping a dyna bomb
14849   {
14850     player->dynabombs_left--;
14851
14852     Tile[dropx][dropy] = new_element;
14853
14854     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14855       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14856                           el2img(Tile[dropx][dropy]), 0);
14857
14858     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14859   }
14860
14861   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14862     InitField_WithBug1(dropx, dropy, FALSE);
14863
14864   new_element = Tile[dropx][dropy];     // element might have changed
14865
14866   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14867       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14868   {
14869     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14870       MovDir[dropx][dropy] = drop_direction;
14871
14872     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14873
14874     // do not cause impact style collision by dropping elements that can fall
14875     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14876   }
14877
14878   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14879   player->is_dropping = TRUE;
14880
14881   player->drop_pressed_delay = 0;
14882   player->is_dropping_pressed = FALSE;
14883
14884   player->drop_x = dropx;
14885   player->drop_y = dropy;
14886
14887   return TRUE;
14888 }
14889
14890 // ----------------------------------------------------------------------------
14891 // game sound playing functions
14892 // ----------------------------------------------------------------------------
14893
14894 static int *loop_sound_frame = NULL;
14895 static int *loop_sound_volume = NULL;
14896
14897 void InitPlayLevelSound(void)
14898 {
14899   int num_sounds = getSoundListSize();
14900
14901   checked_free(loop_sound_frame);
14902   checked_free(loop_sound_volume);
14903
14904   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14905   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14906 }
14907
14908 static void PlayLevelSound(int x, int y, int nr)
14909 {
14910   int sx = SCREENX(x), sy = SCREENY(y);
14911   int volume, stereo_position;
14912   int max_distance = 8;
14913   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14914
14915   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14916       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14917     return;
14918
14919   if (!IN_LEV_FIELD(x, y) ||
14920       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14921       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14922     return;
14923
14924   volume = SOUND_MAX_VOLUME;
14925
14926   if (!IN_SCR_FIELD(sx, sy))
14927   {
14928     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14929     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14930
14931     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14932   }
14933
14934   stereo_position = (SOUND_MAX_LEFT +
14935                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14936                      (SCR_FIELDX + 2 * max_distance));
14937
14938   if (IS_LOOP_SOUND(nr))
14939   {
14940     /* This assures that quieter loop sounds do not overwrite louder ones,
14941        while restarting sound volume comparison with each new game frame. */
14942
14943     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14944       return;
14945
14946     loop_sound_volume[nr] = volume;
14947     loop_sound_frame[nr] = FrameCounter;
14948   }
14949
14950   PlaySoundExt(nr, volume, stereo_position, type);
14951 }
14952
14953 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14954 {
14955   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14956                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14957                  y < LEVELY(BY1) ? LEVELY(BY1) :
14958                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14959                  sound_action);
14960 }
14961
14962 static void PlayLevelSoundAction(int x, int y, int action)
14963 {
14964   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
14965 }
14966
14967 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14968 {
14969   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14970
14971   if (sound_effect != SND_UNDEFINED)
14972     PlayLevelSound(x, y, sound_effect);
14973 }
14974
14975 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14976                                               int action)
14977 {
14978   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14979
14980   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14981     PlayLevelSound(x, y, sound_effect);
14982 }
14983
14984 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14985 {
14986   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14987
14988   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14989     PlayLevelSound(x, y, sound_effect);
14990 }
14991
14992 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14993 {
14994   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14995
14996   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14997     StopSound(sound_effect);
14998 }
14999
15000 static int getLevelMusicNr(void)
15001 {
15002   if (levelset.music[level_nr] != MUS_UNDEFINED)
15003     return levelset.music[level_nr];            // from config file
15004   else
15005     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15006 }
15007
15008 static void FadeLevelSounds(void)
15009 {
15010   FadeSounds();
15011 }
15012
15013 static void FadeLevelMusic(void)
15014 {
15015   int music_nr = getLevelMusicNr();
15016   char *curr_music = getCurrentlyPlayingMusicFilename();
15017   char *next_music = getMusicInfoEntryFilename(music_nr);
15018
15019   if (!strEqual(curr_music, next_music))
15020     FadeMusic();
15021 }
15022
15023 void FadeLevelSoundsAndMusic(void)
15024 {
15025   FadeLevelSounds();
15026   FadeLevelMusic();
15027 }
15028
15029 static void PlayLevelMusic(void)
15030 {
15031   int music_nr = getLevelMusicNr();
15032   char *curr_music = getCurrentlyPlayingMusicFilename();
15033   char *next_music = getMusicInfoEntryFilename(music_nr);
15034
15035   if (!strEqual(curr_music, next_music))
15036     PlayMusicLoop(music_nr);
15037 }
15038
15039 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15040 {
15041   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15042   int offset = 0;
15043   int x = xx - offset;
15044   int y = yy - offset;
15045
15046   switch (sample)
15047   {
15048     case SOUND_blank:
15049       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15050       break;
15051
15052     case SOUND_roll:
15053       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15054       break;
15055
15056     case SOUND_stone:
15057       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15058       break;
15059
15060     case SOUND_nut:
15061       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15062       break;
15063
15064     case SOUND_crack:
15065       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15066       break;
15067
15068     case SOUND_bug:
15069       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15070       break;
15071
15072     case SOUND_tank:
15073       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15074       break;
15075
15076     case SOUND_android_clone:
15077       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15078       break;
15079
15080     case SOUND_android_move:
15081       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15082       break;
15083
15084     case SOUND_spring:
15085       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15086       break;
15087
15088     case SOUND_slurp:
15089       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15090       break;
15091
15092     case SOUND_eater:
15093       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15094       break;
15095
15096     case SOUND_eater_eat:
15097       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15098       break;
15099
15100     case SOUND_alien:
15101       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15102       break;
15103
15104     case SOUND_collect:
15105       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15106       break;
15107
15108     case SOUND_diamond:
15109       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15110       break;
15111
15112     case SOUND_squash:
15113       // !!! CHECK THIS !!!
15114 #if 1
15115       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15116 #else
15117       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15118 #endif
15119       break;
15120
15121     case SOUND_wonderfall:
15122       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15123       break;
15124
15125     case SOUND_drip:
15126       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15127       break;
15128
15129     case SOUND_push:
15130       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15131       break;
15132
15133     case SOUND_dirt:
15134       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15135       break;
15136
15137     case SOUND_acid:
15138       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15139       break;
15140
15141     case SOUND_ball:
15142       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15143       break;
15144
15145     case SOUND_slide:
15146       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15147       break;
15148
15149     case SOUND_wonder:
15150       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15151       break;
15152
15153     case SOUND_door:
15154       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15155       break;
15156
15157     case SOUND_exit_open:
15158       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15159       break;
15160
15161     case SOUND_exit_leave:
15162       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15163       break;
15164
15165     case SOUND_dynamite:
15166       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15167       break;
15168
15169     case SOUND_tick:
15170       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15171       break;
15172
15173     case SOUND_press:
15174       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15175       break;
15176
15177     case SOUND_wheel:
15178       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15179       break;
15180
15181     case SOUND_boom:
15182       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15183       break;
15184
15185     case SOUND_die:
15186       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15187       break;
15188
15189     case SOUND_time:
15190       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15191       break;
15192
15193     default:
15194       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15195       break;
15196   }
15197 }
15198
15199 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15200 {
15201   int element = map_element_SP_to_RND(element_sp);
15202   int action = map_action_SP_to_RND(action_sp);
15203   int offset = (setup.sp_show_border_elements ? 0 : 1);
15204   int x = xx - offset;
15205   int y = yy - offset;
15206
15207   PlayLevelSoundElementAction(x, y, element, action);
15208 }
15209
15210 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15211 {
15212   int element = map_element_MM_to_RND(element_mm);
15213   int action = map_action_MM_to_RND(action_mm);
15214   int offset = 0;
15215   int x = xx - offset;
15216   int y = yy - offset;
15217
15218   if (!IS_MM_ELEMENT(element))
15219     element = EL_MM_DEFAULT;
15220
15221   PlayLevelSoundElementAction(x, y, element, action);
15222 }
15223
15224 void PlaySound_MM(int sound_mm)
15225 {
15226   int sound = map_sound_MM_to_RND(sound_mm);
15227
15228   if (sound == SND_UNDEFINED)
15229     return;
15230
15231   PlaySound(sound);
15232 }
15233
15234 void PlaySoundLoop_MM(int sound_mm)
15235 {
15236   int sound = map_sound_MM_to_RND(sound_mm);
15237
15238   if (sound == SND_UNDEFINED)
15239     return;
15240
15241   PlaySoundLoop(sound);
15242 }
15243
15244 void StopSound_MM(int sound_mm)
15245 {
15246   int sound = map_sound_MM_to_RND(sound_mm);
15247
15248   if (sound == SND_UNDEFINED)
15249     return;
15250
15251   StopSound(sound);
15252 }
15253
15254 void RaiseScore(int value)
15255 {
15256   game.score += value;
15257
15258   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15259
15260   DisplayGameControlValues();
15261 }
15262
15263 void RaiseScoreElement(int element)
15264 {
15265   switch (element)
15266   {
15267     case EL_EMERALD:
15268     case EL_BD_DIAMOND:
15269     case EL_EMERALD_YELLOW:
15270     case EL_EMERALD_RED:
15271     case EL_EMERALD_PURPLE:
15272     case EL_SP_INFOTRON:
15273       RaiseScore(level.score[SC_EMERALD]);
15274       break;
15275     case EL_DIAMOND:
15276       RaiseScore(level.score[SC_DIAMOND]);
15277       break;
15278     case EL_CRYSTAL:
15279       RaiseScore(level.score[SC_CRYSTAL]);
15280       break;
15281     case EL_PEARL:
15282       RaiseScore(level.score[SC_PEARL]);
15283       break;
15284     case EL_BUG:
15285     case EL_BD_BUTTERFLY:
15286     case EL_SP_ELECTRON:
15287       RaiseScore(level.score[SC_BUG]);
15288       break;
15289     case EL_SPACESHIP:
15290     case EL_BD_FIREFLY:
15291     case EL_SP_SNIKSNAK:
15292       RaiseScore(level.score[SC_SPACESHIP]);
15293       break;
15294     case EL_YAMYAM:
15295     case EL_DARK_YAMYAM:
15296       RaiseScore(level.score[SC_YAMYAM]);
15297       break;
15298     case EL_ROBOT:
15299       RaiseScore(level.score[SC_ROBOT]);
15300       break;
15301     case EL_PACMAN:
15302       RaiseScore(level.score[SC_PACMAN]);
15303       break;
15304     case EL_NUT:
15305       RaiseScore(level.score[SC_NUT]);
15306       break;
15307     case EL_DYNAMITE:
15308     case EL_EM_DYNAMITE:
15309     case EL_SP_DISK_RED:
15310     case EL_DYNABOMB_INCREASE_NUMBER:
15311     case EL_DYNABOMB_INCREASE_SIZE:
15312     case EL_DYNABOMB_INCREASE_POWER:
15313       RaiseScore(level.score[SC_DYNAMITE]);
15314       break;
15315     case EL_SHIELD_NORMAL:
15316     case EL_SHIELD_DEADLY:
15317       RaiseScore(level.score[SC_SHIELD]);
15318       break;
15319     case EL_EXTRA_TIME:
15320       RaiseScore(level.extra_time_score);
15321       break;
15322     case EL_KEY_1:
15323     case EL_KEY_2:
15324     case EL_KEY_3:
15325     case EL_KEY_4:
15326     case EL_EM_KEY_1:
15327     case EL_EM_KEY_2:
15328     case EL_EM_KEY_3:
15329     case EL_EM_KEY_4:
15330     case EL_EMC_KEY_5:
15331     case EL_EMC_KEY_6:
15332     case EL_EMC_KEY_7:
15333     case EL_EMC_KEY_8:
15334     case EL_DC_KEY_WHITE:
15335       RaiseScore(level.score[SC_KEY]);
15336       break;
15337     default:
15338       RaiseScore(element_info[element].collect_score);
15339       break;
15340   }
15341 }
15342
15343 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15344 {
15345   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15346   {
15347     // closing door required in case of envelope style request dialogs
15348     if (!skip_request)
15349     {
15350       // prevent short reactivation of overlay buttons while closing door
15351       SetOverlayActive(FALSE);
15352
15353       CloseDoor(DOOR_CLOSE_1);
15354     }
15355
15356     if (network.enabled)
15357       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15358     else
15359     {
15360       if (quick_quit)
15361         FadeSkipNextFadeIn();
15362
15363       SetGameStatus(GAME_MODE_MAIN);
15364
15365       DrawMainMenu();
15366     }
15367   }
15368   else          // continue playing the game
15369   {
15370     if (tape.playing && tape.deactivate_display)
15371       TapeDeactivateDisplayOff(TRUE);
15372
15373     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15374
15375     if (tape.playing && tape.deactivate_display)
15376       TapeDeactivateDisplayOn();
15377   }
15378 }
15379
15380 void RequestQuitGame(boolean ask_if_really_quit)
15381 {
15382   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15383   boolean skip_request = game.all_players_gone || quick_quit;
15384
15385   RequestQuitGameExt(skip_request, quick_quit,
15386                      "Do you really want to quit the game?");
15387 }
15388
15389 void RequestRestartGame(char *message)
15390 {
15391   game.restart_game_message = NULL;
15392
15393   boolean has_started_game = hasStartedNetworkGame();
15394   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15395
15396   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15397   {
15398     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15399   }
15400   else
15401   {
15402     // needed in case of envelope request to close game panel
15403     CloseDoor(DOOR_CLOSE_1);
15404
15405     SetGameStatus(GAME_MODE_MAIN);
15406
15407     DrawMainMenu();
15408   }
15409 }
15410
15411 void CheckGameOver(void)
15412 {
15413   static boolean last_game_over = FALSE;
15414   static int game_over_delay = 0;
15415   int game_over_delay_value = 50;
15416   boolean game_over = checkGameFailed();
15417
15418   // do not handle game over if request dialog is already active
15419   if (game.request_active)
15420     return;
15421
15422   // do not ask to play again if game was never actually played
15423   if (!game.GamePlayed)
15424     return;
15425
15426   if (!game_over)
15427   {
15428     last_game_over = FALSE;
15429     game_over_delay = game_over_delay_value;
15430
15431     return;
15432   }
15433
15434   if (game_over_delay > 0)
15435   {
15436     game_over_delay--;
15437
15438     return;
15439   }
15440
15441   if (last_game_over != game_over)
15442     game.restart_game_message = (hasStartedNetworkGame() ?
15443                                  "Game over! Play it again?" :
15444                                  "Game over!");
15445
15446   last_game_over = game_over;
15447 }
15448
15449 boolean checkGameSolved(void)
15450 {
15451   // set for all game engines if level was solved
15452   return game.LevelSolved_GameEnd;
15453 }
15454
15455 boolean checkGameFailed(void)
15456 {
15457   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15458     return (game_em.game_over && !game_em.level_solved);
15459   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15460     return (game_sp.game_over && !game_sp.level_solved);
15461   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15462     return (game_mm.game_over && !game_mm.level_solved);
15463   else                          // GAME_ENGINE_TYPE_RND
15464     return (game.GameOver && !game.LevelSolved);
15465 }
15466
15467 boolean checkGameEnded(void)
15468 {
15469   return (checkGameSolved() || checkGameFailed());
15470 }
15471
15472
15473 // ----------------------------------------------------------------------------
15474 // random generator functions
15475 // ----------------------------------------------------------------------------
15476
15477 unsigned int InitEngineRandom_RND(int seed)
15478 {
15479   game.num_random_calls = 0;
15480
15481   return InitEngineRandom(seed);
15482 }
15483
15484 unsigned int RND(int max)
15485 {
15486   if (max > 0)
15487   {
15488     game.num_random_calls++;
15489
15490     return GetEngineRandom(max);
15491   }
15492
15493   return 0;
15494 }
15495
15496
15497 // ----------------------------------------------------------------------------
15498 // game engine snapshot handling functions
15499 // ----------------------------------------------------------------------------
15500
15501 struct EngineSnapshotInfo
15502 {
15503   // runtime values for custom element collect score
15504   int collect_score[NUM_CUSTOM_ELEMENTS];
15505
15506   // runtime values for group element choice position
15507   int choice_pos[NUM_GROUP_ELEMENTS];
15508
15509   // runtime values for belt position animations
15510   int belt_graphic[4][NUM_BELT_PARTS];
15511   int belt_anim_mode[4][NUM_BELT_PARTS];
15512 };
15513
15514 static struct EngineSnapshotInfo engine_snapshot_rnd;
15515 static char *snapshot_level_identifier = NULL;
15516 static int snapshot_level_nr = -1;
15517
15518 static void SaveEngineSnapshotValues_RND(void)
15519 {
15520   static int belt_base_active_element[4] =
15521   {
15522     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15523     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15524     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15525     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15526   };
15527   int i, j;
15528
15529   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15530   {
15531     int element = EL_CUSTOM_START + i;
15532
15533     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15534   }
15535
15536   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15537   {
15538     int element = EL_GROUP_START + i;
15539
15540     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15541   }
15542
15543   for (i = 0; i < 4; i++)
15544   {
15545     for (j = 0; j < NUM_BELT_PARTS; j++)
15546     {
15547       int element = belt_base_active_element[i] + j;
15548       int graphic = el2img(element);
15549       int anim_mode = graphic_info[graphic].anim_mode;
15550
15551       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15552       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15553     }
15554   }
15555 }
15556
15557 static void LoadEngineSnapshotValues_RND(void)
15558 {
15559   unsigned int num_random_calls = game.num_random_calls;
15560   int i, j;
15561
15562   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15563   {
15564     int element = EL_CUSTOM_START + i;
15565
15566     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15567   }
15568
15569   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15570   {
15571     int element = EL_GROUP_START + i;
15572
15573     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15574   }
15575
15576   for (i = 0; i < 4; i++)
15577   {
15578     for (j = 0; j < NUM_BELT_PARTS; j++)
15579     {
15580       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15581       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15582
15583       graphic_info[graphic].anim_mode = anim_mode;
15584     }
15585   }
15586
15587   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15588   {
15589     InitRND(tape.random_seed);
15590     for (i = 0; i < num_random_calls; i++)
15591       RND(1);
15592   }
15593
15594   if (game.num_random_calls != num_random_calls)
15595   {
15596     Error("number of random calls out of sync");
15597     Error("number of random calls should be %d", num_random_calls);
15598     Error("number of random calls is %d", game.num_random_calls);
15599
15600     Fail("this should not happen -- please debug");
15601   }
15602 }
15603
15604 void FreeEngineSnapshotSingle(void)
15605 {
15606   FreeSnapshotSingle();
15607
15608   setString(&snapshot_level_identifier, NULL);
15609   snapshot_level_nr = -1;
15610 }
15611
15612 void FreeEngineSnapshotList(void)
15613 {
15614   FreeSnapshotList();
15615 }
15616
15617 static ListNode *SaveEngineSnapshotBuffers(void)
15618 {
15619   ListNode *buffers = NULL;
15620
15621   // copy some special values to a structure better suited for the snapshot
15622
15623   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15624     SaveEngineSnapshotValues_RND();
15625   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15626     SaveEngineSnapshotValues_EM();
15627   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15628     SaveEngineSnapshotValues_SP(&buffers);
15629   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15630     SaveEngineSnapshotValues_MM(&buffers);
15631
15632   // save values stored in special snapshot structure
15633
15634   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15635     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15636   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15637     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15638   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15639     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15640   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15641     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15642
15643   // save further RND engine values
15644
15645   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15646   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15647   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15648
15649   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15650   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15651   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15652   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15653   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15654
15655   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15656   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15657   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15658
15659   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15660
15661   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15662   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15663
15664   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15665   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15666   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15667   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15668   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15669   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15670   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15671   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15672   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15673   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15674   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15675   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15676   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15677   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15678   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15679   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15680   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15681   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15682
15683   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15684   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15685
15686   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15687   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15688   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15689
15690   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15691   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15692
15693   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15694   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15696   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15697   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15698
15699   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15700   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15701
15702 #if 0
15703   ListNode *node = engine_snapshot_list_rnd;
15704   int num_bytes = 0;
15705
15706   while (node != NULL)
15707   {
15708     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15709
15710     node = node->next;
15711   }
15712
15713   Debug("game:playing:SaveEngineSnapshotBuffers",
15714         "size of engine snapshot: %d bytes", num_bytes);
15715 #endif
15716
15717   return buffers;
15718 }
15719
15720 void SaveEngineSnapshotSingle(void)
15721 {
15722   ListNode *buffers = SaveEngineSnapshotBuffers();
15723
15724   // finally save all snapshot buffers to single snapshot
15725   SaveSnapshotSingle(buffers);
15726
15727   // save level identification information
15728   setString(&snapshot_level_identifier, leveldir_current->identifier);
15729   snapshot_level_nr = level_nr;
15730 }
15731
15732 boolean CheckSaveEngineSnapshotToList(void)
15733 {
15734   boolean save_snapshot =
15735     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15736      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15737       game.snapshot.changed_action) ||
15738      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15739       game.snapshot.collected_item));
15740
15741   game.snapshot.changed_action = FALSE;
15742   game.snapshot.collected_item = FALSE;
15743   game.snapshot.save_snapshot = save_snapshot;
15744
15745   return save_snapshot;
15746 }
15747
15748 void SaveEngineSnapshotToList(void)
15749 {
15750   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15751       tape.quick_resume)
15752     return;
15753
15754   ListNode *buffers = SaveEngineSnapshotBuffers();
15755
15756   // finally save all snapshot buffers to snapshot list
15757   SaveSnapshotToList(buffers);
15758 }
15759
15760 void SaveEngineSnapshotToListInitial(void)
15761 {
15762   FreeEngineSnapshotList();
15763
15764   SaveEngineSnapshotToList();
15765 }
15766
15767 static void LoadEngineSnapshotValues(void)
15768 {
15769   // restore special values from snapshot structure
15770
15771   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15772     LoadEngineSnapshotValues_RND();
15773   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15774     LoadEngineSnapshotValues_EM();
15775   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15776     LoadEngineSnapshotValues_SP();
15777   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15778     LoadEngineSnapshotValues_MM();
15779 }
15780
15781 void LoadEngineSnapshotSingle(void)
15782 {
15783   LoadSnapshotSingle();
15784
15785   LoadEngineSnapshotValues();
15786 }
15787
15788 static void LoadEngineSnapshot_Undo(int steps)
15789 {
15790   LoadSnapshotFromList_Older(steps);
15791
15792   LoadEngineSnapshotValues();
15793 }
15794
15795 static void LoadEngineSnapshot_Redo(int steps)
15796 {
15797   LoadSnapshotFromList_Newer(steps);
15798
15799   LoadEngineSnapshotValues();
15800 }
15801
15802 boolean CheckEngineSnapshotSingle(void)
15803 {
15804   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15805           snapshot_level_nr == level_nr);
15806 }
15807
15808 boolean CheckEngineSnapshotList(void)
15809 {
15810   return CheckSnapshotList();
15811 }
15812
15813
15814 // ---------- new game button stuff -------------------------------------------
15815
15816 static struct
15817 {
15818   int graphic;
15819   struct XY *pos;
15820   int gadget_id;
15821   boolean *setup_value;
15822   boolean allowed_on_tape;
15823   boolean is_touch_button;
15824   char *infotext;
15825 } gamebutton_info[NUM_GAME_BUTTONS] =
15826 {
15827   {
15828     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15829     GAME_CTRL_ID_STOP,                          NULL,
15830     TRUE, FALSE,                                "stop game"
15831   },
15832   {
15833     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15834     GAME_CTRL_ID_PAUSE,                         NULL,
15835     TRUE, FALSE,                                "pause game"
15836   },
15837   {
15838     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15839     GAME_CTRL_ID_PLAY,                          NULL,
15840     TRUE, FALSE,                                "play game"
15841   },
15842   {
15843     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15844     GAME_CTRL_ID_UNDO,                          NULL,
15845     TRUE, FALSE,                                "undo step"
15846   },
15847   {
15848     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15849     GAME_CTRL_ID_REDO,                          NULL,
15850     TRUE, FALSE,                                "redo step"
15851   },
15852   {
15853     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15854     GAME_CTRL_ID_SAVE,                          NULL,
15855     TRUE, FALSE,                                "save game"
15856   },
15857   {
15858     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15859     GAME_CTRL_ID_PAUSE2,                        NULL,
15860     TRUE, FALSE,                                "pause game"
15861   },
15862   {
15863     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15864     GAME_CTRL_ID_LOAD,                          NULL,
15865     TRUE, FALSE,                                "load game"
15866   },
15867   {
15868     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15869     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15870     FALSE, FALSE,                               "stop game"
15871   },
15872   {
15873     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15874     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15875     FALSE, FALSE,                               "pause game"
15876   },
15877   {
15878     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15879     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15880     FALSE, FALSE,                               "play game"
15881   },
15882   {
15883     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15884     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15885     FALSE, TRUE,                                "stop game"
15886   },
15887   {
15888     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15889     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15890     FALSE, TRUE,                                "pause game"
15891   },
15892   {
15893     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15894     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15895     TRUE, FALSE,                                "background music on/off"
15896   },
15897   {
15898     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15899     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15900     TRUE, FALSE,                                "sound loops on/off"
15901   },
15902   {
15903     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15904     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15905     TRUE, FALSE,                                "normal sounds on/off"
15906   },
15907   {
15908     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15909     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15910     FALSE, FALSE,                               "background music on/off"
15911   },
15912   {
15913     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15914     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15915     FALSE, FALSE,                               "sound loops on/off"
15916   },
15917   {
15918     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15919     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15920     FALSE, FALSE,                               "normal sounds on/off"
15921   }
15922 };
15923
15924 void CreateGameButtons(void)
15925 {
15926   int i;
15927
15928   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15929   {
15930     int graphic = gamebutton_info[i].graphic;
15931     struct GraphicInfo *gfx = &graphic_info[graphic];
15932     struct XY *pos = gamebutton_info[i].pos;
15933     struct GadgetInfo *gi;
15934     int button_type;
15935     boolean checked;
15936     unsigned int event_mask;
15937     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15938     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15939     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15940     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15941     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15942     int gd_x   = gfx->src_x;
15943     int gd_y   = gfx->src_y;
15944     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15945     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15946     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15947     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15948     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15949     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15950     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15951     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15952     int id = i;
15953
15954     if (gfx->bitmap == NULL)
15955     {
15956       game_gadget[id] = NULL;
15957
15958       continue;
15959     }
15960
15961     if (id == GAME_CTRL_ID_STOP ||
15962         id == GAME_CTRL_ID_PANEL_STOP ||
15963         id == GAME_CTRL_ID_TOUCH_STOP ||
15964         id == GAME_CTRL_ID_PLAY ||
15965         id == GAME_CTRL_ID_PANEL_PLAY ||
15966         id == GAME_CTRL_ID_SAVE ||
15967         id == GAME_CTRL_ID_LOAD)
15968     {
15969       button_type = GD_TYPE_NORMAL_BUTTON;
15970       checked = FALSE;
15971       event_mask = GD_EVENT_RELEASED;
15972     }
15973     else if (id == GAME_CTRL_ID_UNDO ||
15974              id == GAME_CTRL_ID_REDO)
15975     {
15976       button_type = GD_TYPE_NORMAL_BUTTON;
15977       checked = FALSE;
15978       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15979     }
15980     else
15981     {
15982       button_type = GD_TYPE_CHECK_BUTTON;
15983       checked = (gamebutton_info[i].setup_value != NULL ?
15984                  *gamebutton_info[i].setup_value : FALSE);
15985       event_mask = GD_EVENT_PRESSED;
15986     }
15987
15988     gi = CreateGadget(GDI_CUSTOM_ID, id,
15989                       GDI_IMAGE_ID, graphic,
15990                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15991                       GDI_X, base_x + x,
15992                       GDI_Y, base_y + y,
15993                       GDI_WIDTH, gfx->width,
15994                       GDI_HEIGHT, gfx->height,
15995                       GDI_TYPE, button_type,
15996                       GDI_STATE, GD_BUTTON_UNPRESSED,
15997                       GDI_CHECKED, checked,
15998                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15999                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16000                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16001                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16002                       GDI_DIRECT_DRAW, FALSE,
16003                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16004                       GDI_EVENT_MASK, event_mask,
16005                       GDI_CALLBACK_ACTION, HandleGameButtons,
16006                       GDI_END);
16007
16008     if (gi == NULL)
16009       Fail("cannot create gadget");
16010
16011     game_gadget[id] = gi;
16012   }
16013 }
16014
16015 void FreeGameButtons(void)
16016 {
16017   int i;
16018
16019   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16020     FreeGadget(game_gadget[i]);
16021 }
16022
16023 static void UnmapGameButtonsAtSamePosition(int id)
16024 {
16025   int i;
16026
16027   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16028     if (i != id &&
16029         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16030         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16031       UnmapGadget(game_gadget[i]);
16032 }
16033
16034 static void UnmapGameButtonsAtSamePosition_All(void)
16035 {
16036   if (setup.show_snapshot_buttons)
16037   {
16038     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16039     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16040     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16041   }
16042   else
16043   {
16044     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16045     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16046     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16047
16048     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16049     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16050     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16051   }
16052 }
16053
16054 static void MapGameButtonsAtSamePosition(int id)
16055 {
16056   int i;
16057
16058   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16059     if (i != id &&
16060         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16061         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16062       MapGadget(game_gadget[i]);
16063
16064   UnmapGameButtonsAtSamePosition_All();
16065 }
16066
16067 void MapUndoRedoButtons(void)
16068 {
16069   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16070   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16071
16072   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16073   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16074 }
16075
16076 void UnmapUndoRedoButtons(void)
16077 {
16078   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16079   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16080
16081   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16082   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16083 }
16084
16085 void ModifyPauseButtons(void)
16086 {
16087   static int ids[] =
16088   {
16089     GAME_CTRL_ID_PAUSE,
16090     GAME_CTRL_ID_PAUSE2,
16091     GAME_CTRL_ID_PANEL_PAUSE,
16092     GAME_CTRL_ID_TOUCH_PAUSE,
16093     -1
16094   };
16095   int i;
16096
16097   for (i = 0; ids[i] > -1; i++)
16098     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16099 }
16100
16101 static void MapGameButtonsExt(boolean on_tape)
16102 {
16103   int i;
16104
16105   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16106     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16107         i != GAME_CTRL_ID_UNDO &&
16108         i != GAME_CTRL_ID_REDO)
16109       MapGadget(game_gadget[i]);
16110
16111   UnmapGameButtonsAtSamePosition_All();
16112
16113   RedrawGameButtons();
16114 }
16115
16116 static void UnmapGameButtonsExt(boolean on_tape)
16117 {
16118   int i;
16119
16120   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16121     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16122       UnmapGadget(game_gadget[i]);
16123 }
16124
16125 static void RedrawGameButtonsExt(boolean on_tape)
16126 {
16127   int i;
16128
16129   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16130     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16131       RedrawGadget(game_gadget[i]);
16132 }
16133
16134 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16135 {
16136   if (gi == NULL)
16137     return;
16138
16139   gi->checked = state;
16140 }
16141
16142 static void RedrawSoundButtonGadget(int id)
16143 {
16144   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16145              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16146              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16147              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16148              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16149              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16150              id);
16151
16152   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16153   RedrawGadget(game_gadget[id2]);
16154 }
16155
16156 void MapGameButtons(void)
16157 {
16158   MapGameButtonsExt(FALSE);
16159 }
16160
16161 void UnmapGameButtons(void)
16162 {
16163   UnmapGameButtonsExt(FALSE);
16164 }
16165
16166 void RedrawGameButtons(void)
16167 {
16168   RedrawGameButtonsExt(FALSE);
16169 }
16170
16171 void MapGameButtonsOnTape(void)
16172 {
16173   MapGameButtonsExt(TRUE);
16174 }
16175
16176 void UnmapGameButtonsOnTape(void)
16177 {
16178   UnmapGameButtonsExt(TRUE);
16179 }
16180
16181 void RedrawGameButtonsOnTape(void)
16182 {
16183   RedrawGameButtonsExt(TRUE);
16184 }
16185
16186 static void GameUndoRedoExt(void)
16187 {
16188   ClearPlayerAction();
16189
16190   tape.pausing = TRUE;
16191
16192   RedrawPlayfield();
16193   UpdateAndDisplayGameControlValues();
16194
16195   DrawCompleteVideoDisplay();
16196   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16197   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16198   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16199
16200   BackToFront();
16201 }
16202
16203 static void GameUndo(int steps)
16204 {
16205   if (!CheckEngineSnapshotList())
16206     return;
16207
16208   LoadEngineSnapshot_Undo(steps);
16209
16210   GameUndoRedoExt();
16211 }
16212
16213 static void GameRedo(int steps)
16214 {
16215   if (!CheckEngineSnapshotList())
16216     return;
16217
16218   LoadEngineSnapshot_Redo(steps);
16219
16220   GameUndoRedoExt();
16221 }
16222
16223 static void HandleGameButtonsExt(int id, int button)
16224 {
16225   static boolean game_undo_executed = FALSE;
16226   int steps = BUTTON_STEPSIZE(button);
16227   boolean handle_game_buttons =
16228     (game_status == GAME_MODE_PLAYING ||
16229      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16230
16231   if (!handle_game_buttons)
16232     return;
16233
16234   switch (id)
16235   {
16236     case GAME_CTRL_ID_STOP:
16237     case GAME_CTRL_ID_PANEL_STOP:
16238     case GAME_CTRL_ID_TOUCH_STOP:
16239       if (game_status == GAME_MODE_MAIN)
16240         break;
16241
16242       if (tape.playing)
16243         TapeStop();
16244       else
16245         RequestQuitGame(TRUE);
16246
16247       break;
16248
16249     case GAME_CTRL_ID_PAUSE:
16250     case GAME_CTRL_ID_PAUSE2:
16251     case GAME_CTRL_ID_PANEL_PAUSE:
16252     case GAME_CTRL_ID_TOUCH_PAUSE:
16253       if (network.enabled && game_status == GAME_MODE_PLAYING)
16254       {
16255         if (tape.pausing)
16256           SendToServer_ContinuePlaying();
16257         else
16258           SendToServer_PausePlaying();
16259       }
16260       else
16261         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16262
16263       game_undo_executed = FALSE;
16264
16265       break;
16266
16267     case GAME_CTRL_ID_PLAY:
16268     case GAME_CTRL_ID_PANEL_PLAY:
16269       if (game_status == GAME_MODE_MAIN)
16270       {
16271         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16272       }
16273       else if (tape.pausing)
16274       {
16275         if (network.enabled)
16276           SendToServer_ContinuePlaying();
16277         else
16278           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16279       }
16280       break;
16281
16282     case GAME_CTRL_ID_UNDO:
16283       // Important: When using "save snapshot when collecting an item" mode,
16284       // load last (current) snapshot for first "undo" after pressing "pause"
16285       // (else the last-but-one snapshot would be loaded, because the snapshot
16286       // pointer already points to the last snapshot when pressing "pause",
16287       // which is fine for "every step/move" mode, but not for "every collect")
16288       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16289           !game_undo_executed)
16290         steps--;
16291
16292       game_undo_executed = TRUE;
16293
16294       GameUndo(steps);
16295       break;
16296
16297     case GAME_CTRL_ID_REDO:
16298       GameRedo(steps);
16299       break;
16300
16301     case GAME_CTRL_ID_SAVE:
16302       TapeQuickSave();
16303       break;
16304
16305     case GAME_CTRL_ID_LOAD:
16306       TapeQuickLoad();
16307       break;
16308
16309     case SOUND_CTRL_ID_MUSIC:
16310     case SOUND_CTRL_ID_PANEL_MUSIC:
16311       if (setup.sound_music)
16312       { 
16313         setup.sound_music = FALSE;
16314
16315         FadeMusic();
16316       }
16317       else if (audio.music_available)
16318       { 
16319         setup.sound = setup.sound_music = TRUE;
16320
16321         SetAudioMode(setup.sound);
16322
16323         if (game_status == GAME_MODE_PLAYING)
16324           PlayLevelMusic();
16325       }
16326
16327       RedrawSoundButtonGadget(id);
16328
16329       break;
16330
16331     case SOUND_CTRL_ID_LOOPS:
16332     case SOUND_CTRL_ID_PANEL_LOOPS:
16333       if (setup.sound_loops)
16334         setup.sound_loops = FALSE;
16335       else if (audio.loops_available)
16336       {
16337         setup.sound = setup.sound_loops = TRUE;
16338
16339         SetAudioMode(setup.sound);
16340       }
16341
16342       RedrawSoundButtonGadget(id);
16343
16344       break;
16345
16346     case SOUND_CTRL_ID_SIMPLE:
16347     case SOUND_CTRL_ID_PANEL_SIMPLE:
16348       if (setup.sound_simple)
16349         setup.sound_simple = FALSE;
16350       else if (audio.sound_available)
16351       {
16352         setup.sound = setup.sound_simple = TRUE;
16353
16354         SetAudioMode(setup.sound);
16355       }
16356
16357       RedrawSoundButtonGadget(id);
16358
16359       break;
16360
16361     default:
16362       break;
16363   }
16364 }
16365
16366 static void HandleGameButtons(struct GadgetInfo *gi)
16367 {
16368   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16369 }
16370
16371 void HandleSoundButtonKeys(Key key)
16372 {
16373   if (key == setup.shortcut.sound_simple)
16374     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16375   else if (key == setup.shortcut.sound_loops)
16376     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16377   else if (key == setup.shortcut.sound_music)
16378     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16379 }