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