moved setting auto-exit for Sokoban-style levels from runtime to level
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void TestIfPlayerTouchesCustomElement(int, int);
1062 static void TestIfElementTouchesCustomElement(int, int);
1063 static void TestIfElementHitsCustomElement(int, int, int);
1064
1065 static void HandleElementChange(int, int, int);
1066 static void ExecuteCustomElementAction(int, int, int, int);
1067 static boolean ChangeElement(int, int, int, int);
1068
1069 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1070 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1072 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1073         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1074 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1075         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1076 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1077         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1078 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1079         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1080
1081 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1082 #define CheckElementChange(x, y, e, te, ev)                             \
1083         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1084 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1085         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1086 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1087         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1088 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1089         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1090
1091 static void PlayLevelSound(int, int, int);
1092 static void PlayLevelSoundNearest(int, int, int);
1093 static void PlayLevelSoundAction(int, int, int);
1094 static void PlayLevelSoundElementAction(int, int, int, int);
1095 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1096 static void PlayLevelSoundActionIfLoop(int, int, int);
1097 static void StopLevelSoundActionIfLoop(int, int, int);
1098 static void PlayLevelMusic(void);
1099 static void FadeLevelSoundsAndMusic(void);
1100
1101 static void HandleGameButtons(struct GadgetInfo *);
1102
1103 int AmoebaNeighbourNr(int, int);
1104 void AmoebaToDiamond(int, int);
1105 void ContinueMoving(int, int);
1106 void Bang(int, int);
1107 void InitMovDir(int, int);
1108 void InitAmoebaNr(int, int);
1109 void NewHighScore(int);
1110
1111 void TestIfGoodThingHitsBadThing(int, int, int);
1112 void TestIfBadThingHitsGoodThing(int, int, int);
1113 void TestIfPlayerTouchesBadThing(int, int);
1114 void TestIfPlayerRunsIntoBadThing(int, int, int);
1115 void TestIfBadThingTouchesPlayer(int, int);
1116 void TestIfBadThingRunsIntoPlayer(int, int, int);
1117 void TestIfFriendTouchesBadThing(int, int);
1118 void TestIfBadThingTouchesFriend(int, int);
1119 void TestIfBadThingTouchesOtherBadThing(int, int);
1120 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1121
1122 void KillPlayer(struct PlayerInfo *);
1123 void BuryPlayer(struct PlayerInfo *);
1124 void RemovePlayer(struct PlayerInfo *);
1125 void ExitPlayer(struct PlayerInfo *);
1126
1127 static int getInvisibleActiveFromInvisibleElement(int);
1128 static int getInvisibleFromInvisibleActiveElement(int);
1129
1130 static void TestFieldAfterSnapping(int, int, int, int, int);
1131
1132 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1133
1134 // for detection of endless loops, caused by custom element programming
1135 // (using maximal playfield width x 10 is just a rough approximation)
1136 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1137
1138 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1139 {                                                                       \
1140   if (recursion_loop_detected)                                          \
1141     return (rc);                                                        \
1142                                                                         \
1143   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1144   {                                                                     \
1145     recursion_loop_detected = TRUE;                                     \
1146     recursion_loop_element = (e);                                       \
1147   }                                                                     \
1148                                                                         \
1149   recursion_loop_depth++;                                               \
1150 }
1151
1152 #define RECURSION_LOOP_DETECTION_END()                                  \
1153 {                                                                       \
1154   recursion_loop_depth--;                                               \
1155 }
1156
1157 static int recursion_loop_depth;
1158 static boolean recursion_loop_detected;
1159 static boolean recursion_loop_element;
1160
1161 static int map_player_action[MAX_PLAYERS];
1162
1163
1164 // ----------------------------------------------------------------------------
1165 // definition of elements that automatically change to other elements after
1166 // a specified time, eventually calling a function when changing
1167 // ----------------------------------------------------------------------------
1168
1169 // forward declaration for changer functions
1170 static void InitBuggyBase(int, int);
1171 static void WarnBuggyBase(int, int);
1172
1173 static void InitTrap(int, int);
1174 static void ActivateTrap(int, int);
1175 static void ChangeActiveTrap(int, int);
1176
1177 static void InitRobotWheel(int, int);
1178 static void RunRobotWheel(int, int);
1179 static void StopRobotWheel(int, int);
1180
1181 static void InitTimegateWheel(int, int);
1182 static void RunTimegateWheel(int, int);
1183
1184 static void InitMagicBallDelay(int, int);
1185 static void ActivateMagicBall(int, int);
1186
1187 struct ChangingElementInfo
1188 {
1189   int element;
1190   int target_element;
1191   int change_delay;
1192   void (*pre_change_function)(int x, int y);
1193   void (*change_function)(int x, int y);
1194   void (*post_change_function)(int x, int y);
1195 };
1196
1197 static struct ChangingElementInfo change_delay_list[] =
1198 {
1199   {
1200     EL_NUT_BREAKING,
1201     EL_EMERALD,
1202     6,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_PEARL_BREAKING,
1209     EL_EMPTY,
1210     8,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EXIT_OPENING,
1217     EL_EXIT_OPEN,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_EXIT_CLOSING,
1225     EL_EXIT_CLOSED,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_STEEL_EXIT_OPENING,
1233     EL_STEEL_EXIT_OPEN,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_STEEL_EXIT_CLOSING,
1241     EL_STEEL_EXIT_CLOSED,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EM_EXIT_OPENING,
1249     EL_EM_EXIT_OPEN,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_EM_EXIT_CLOSING,
1257     EL_EMPTY,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_EM_STEEL_EXIT_OPENING,
1265     EL_EM_STEEL_EXIT_OPEN,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_EM_STEEL_EXIT_CLOSING,
1273     EL_STEELWALL,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_SP_EXIT_OPENING,
1281     EL_SP_EXIT_OPEN,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SP_EXIT_CLOSING,
1289     EL_SP_EXIT_CLOSED,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SWITCHGATE_OPENING,
1297     EL_SWITCHGATE_OPEN,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SWITCHGATE_CLOSING,
1305     EL_SWITCHGATE_CLOSED,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_TIMEGATE_OPENING,
1313     EL_TIMEGATE_OPEN,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319   {
1320     EL_TIMEGATE_CLOSING,
1321     EL_TIMEGATE_CLOSED,
1322     29,
1323     NULL,
1324     NULL,
1325     NULL
1326   },
1327
1328   {
1329     EL_ACID_SPLASH_LEFT,
1330     EL_EMPTY,
1331     8,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_ACID_SPLASH_RIGHT,
1338     EL_EMPTY,
1339     8,
1340     NULL,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_SP_BUGGY_BASE,
1346     EL_SP_BUGGY_BASE_ACTIVATING,
1347     0,
1348     InitBuggyBase,
1349     NULL,
1350     NULL
1351   },
1352   {
1353     EL_SP_BUGGY_BASE_ACTIVATING,
1354     EL_SP_BUGGY_BASE_ACTIVE,
1355     0,
1356     InitBuggyBase,
1357     NULL,
1358     NULL
1359   },
1360   {
1361     EL_SP_BUGGY_BASE_ACTIVE,
1362     EL_SP_BUGGY_BASE,
1363     0,
1364     InitBuggyBase,
1365     WarnBuggyBase,
1366     NULL
1367   },
1368   {
1369     EL_TRAP,
1370     EL_TRAP_ACTIVE,
1371     0,
1372     InitTrap,
1373     NULL,
1374     ActivateTrap
1375   },
1376   {
1377     EL_TRAP_ACTIVE,
1378     EL_TRAP,
1379     31,
1380     NULL,
1381     ChangeActiveTrap,
1382     NULL
1383   },
1384   {
1385     EL_ROBOT_WHEEL_ACTIVE,
1386     EL_ROBOT_WHEEL,
1387     0,
1388     InitRobotWheel,
1389     RunRobotWheel,
1390     StopRobotWheel
1391   },
1392   {
1393     EL_TIMEGATE_SWITCH_ACTIVE,
1394     EL_TIMEGATE_SWITCH,
1395     0,
1396     InitTimegateWheel,
1397     RunTimegateWheel,
1398     NULL
1399   },
1400   {
1401     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1402     EL_DC_TIMEGATE_SWITCH,
1403     0,
1404     InitTimegateWheel,
1405     RunTimegateWheel,
1406     NULL
1407   },
1408   {
1409     EL_EMC_MAGIC_BALL_ACTIVE,
1410     EL_EMC_MAGIC_BALL_ACTIVE,
1411     0,
1412     InitMagicBallDelay,
1413     NULL,
1414     ActivateMagicBall
1415   },
1416   {
1417     EL_EMC_SPRING_BUMPER_ACTIVE,
1418     EL_EMC_SPRING_BUMPER,
1419     8,
1420     NULL,
1421     NULL,
1422     NULL
1423   },
1424   {
1425     EL_DIAGONAL_SHRINKING,
1426     EL_UNDEFINED,
1427     0,
1428     NULL,
1429     NULL,
1430     NULL
1431   },
1432   {
1433     EL_DIAGONAL_GROWING,
1434     EL_UNDEFINED,
1435     0,
1436     NULL,
1437     NULL,
1438     NULL,
1439   },
1440
1441   {
1442     EL_UNDEFINED,
1443     EL_UNDEFINED,
1444     -1,
1445     NULL,
1446     NULL,
1447     NULL
1448   }
1449 };
1450
1451 struct
1452 {
1453   int element;
1454   int push_delay_fixed, push_delay_random;
1455 }
1456 push_delay_list[] =
1457 {
1458   { EL_SPRING,                  0, 0 },
1459   { EL_BALLOON,                 0, 0 },
1460
1461   { EL_SOKOBAN_OBJECT,          2, 0 },
1462   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1463   { EL_SATELLITE,               2, 0 },
1464   { EL_SP_DISK_YELLOW,          2, 0 },
1465
1466   { EL_UNDEFINED,               0, 0 },
1467 };
1468
1469 struct
1470 {
1471   int element;
1472   int move_stepsize;
1473 }
1474 move_stepsize_list[] =
1475 {
1476   { EL_AMOEBA_DROP,             2 },
1477   { EL_AMOEBA_DROPPING,         2 },
1478   { EL_QUICKSAND_FILLING,       1 },
1479   { EL_QUICKSAND_EMPTYING,      1 },
1480   { EL_QUICKSAND_FAST_FILLING,  2 },
1481   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1482   { EL_MAGIC_WALL_FILLING,      2 },
1483   { EL_MAGIC_WALL_EMPTYING,     2 },
1484   { EL_BD_MAGIC_WALL_FILLING,   2 },
1485   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1486   { EL_DC_MAGIC_WALL_FILLING,   2 },
1487   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1488
1489   { EL_UNDEFINED,               0 },
1490 };
1491
1492 struct
1493 {
1494   int element;
1495   int count;
1496 }
1497 collect_count_list[] =
1498 {
1499   { EL_EMERALD,                 1 },
1500   { EL_BD_DIAMOND,              1 },
1501   { EL_EMERALD_YELLOW,          1 },
1502   { EL_EMERALD_RED,             1 },
1503   { EL_EMERALD_PURPLE,          1 },
1504   { EL_DIAMOND,                 3 },
1505   { EL_SP_INFOTRON,             1 },
1506   { EL_PEARL,                   5 },
1507   { EL_CRYSTAL,                 8 },
1508
1509   { EL_UNDEFINED,               0 },
1510 };
1511
1512 struct
1513 {
1514   int element;
1515   int direction;
1516 }
1517 access_direction_list[] =
1518 {
1519   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1520   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1521   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1522   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1525   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1526   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1527   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1528   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1529   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1530
1531   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1532   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1533   { EL_SP_PORT_UP,                                                   MV_DOWN },
1534   { EL_SP_PORT_DOWN,                                         MV_UP           },
1535   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1536   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1537   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1538   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1539   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1540   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1542   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1543   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1544   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1545   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1546   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1547   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1548   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1549   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1550
1551   { EL_UNDEFINED,                       MV_NONE                              }
1552 };
1553
1554 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1555
1556 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1557 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1558 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1559                                  IS_JUST_CHANGING(x, y))
1560
1561 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1562
1563 // static variables for playfield scan mode (scanning forward or backward)
1564 static int playfield_scan_start_x = 0;
1565 static int playfield_scan_start_y = 0;
1566 static int playfield_scan_delta_x = 1;
1567 static int playfield_scan_delta_y = 1;
1568
1569 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1570                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1571                                      (y) += playfield_scan_delta_y)     \
1572                                 for ((x) = playfield_scan_start_x;      \
1573                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1574                                      (x) += playfield_scan_delta_x)
1575
1576 #ifdef DEBUG
1577 void DEBUG_SetMaximumDynamite(void)
1578 {
1579   int i;
1580
1581   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1582     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1583       local_player->inventory_element[local_player->inventory_size++] =
1584         EL_DYNAMITE;
1585 }
1586 #endif
1587
1588 static void InitPlayfieldScanModeVars(void)
1589 {
1590   if (game.use_reverse_scan_direction)
1591   {
1592     playfield_scan_start_x = lev_fieldx - 1;
1593     playfield_scan_start_y = lev_fieldy - 1;
1594
1595     playfield_scan_delta_x = -1;
1596     playfield_scan_delta_y = -1;
1597   }
1598   else
1599   {
1600     playfield_scan_start_x = 0;
1601     playfield_scan_start_y = 0;
1602
1603     playfield_scan_delta_x = 1;
1604     playfield_scan_delta_y = 1;
1605   }
1606 }
1607
1608 static void InitPlayfieldScanMode(int mode)
1609 {
1610   game.use_reverse_scan_direction =
1611     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1612
1613   InitPlayfieldScanModeVars();
1614 }
1615
1616 static int get_move_delay_from_stepsize(int move_stepsize)
1617 {
1618   move_stepsize =
1619     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1620
1621   // make sure that stepsize value is always a power of 2
1622   move_stepsize = (1 << log_2(move_stepsize));
1623
1624   return TILEX / move_stepsize;
1625 }
1626
1627 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1628                                boolean init_game)
1629 {
1630   int player_nr = player->index_nr;
1631   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1632   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1633
1634   // do no immediately change move delay -- the player might just be moving
1635   player->move_delay_value_next = move_delay;
1636
1637   // information if player can move must be set separately
1638   player->cannot_move = cannot_move;
1639
1640   if (init_game)
1641   {
1642     player->move_delay       = game.initial_move_delay[player_nr];
1643     player->move_delay_value = game.initial_move_delay_value[player_nr];
1644
1645     player->move_delay_value_next = -1;
1646
1647     player->move_delay_reset_counter = 0;
1648   }
1649 }
1650
1651 void GetPlayerConfig(void)
1652 {
1653   GameFrameDelay = setup.game_frame_delay;
1654
1655   if (!audio.sound_available)
1656     setup.sound_simple = FALSE;
1657
1658   if (!audio.loops_available)
1659     setup.sound_loops = FALSE;
1660
1661   if (!audio.music_available)
1662     setup.sound_music = FALSE;
1663
1664   if (!video.fullscreen_available)
1665     setup.fullscreen = FALSE;
1666
1667   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1668
1669   SetAudioMode(setup.sound);
1670 }
1671
1672 int GetElementFromGroupElement(int element)
1673 {
1674   if (IS_GROUP_ELEMENT(element))
1675   {
1676     struct ElementGroupInfo *group = element_info[element].group;
1677     int last_anim_random_frame = gfx.anim_random_frame;
1678     int element_pos;
1679
1680     if (group->choice_mode == ANIM_RANDOM)
1681       gfx.anim_random_frame = RND(group->num_elements_resolved);
1682
1683     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1684                                     group->choice_mode, 0,
1685                                     group->choice_pos);
1686
1687     if (group->choice_mode == ANIM_RANDOM)
1688       gfx.anim_random_frame = last_anim_random_frame;
1689
1690     group->choice_pos++;
1691
1692     element = group->element_resolved[element_pos];
1693   }
1694
1695   return element;
1696 }
1697
1698 static void IncrementSokobanFieldsNeeded(void)
1699 {
1700   if (level.sb_fields_needed)
1701     game.sokoban_fields_still_needed++;
1702 }
1703
1704 static void IncrementSokobanObjectsNeeded(void)
1705 {
1706   if (level.sb_objects_needed)
1707     game.sokoban_objects_still_needed++;
1708 }
1709
1710 static void DecrementSokobanFieldsNeeded(void)
1711 {
1712   if (game.sokoban_fields_still_needed > 0)
1713     game.sokoban_fields_still_needed--;
1714 }
1715
1716 static void DecrementSokobanObjectsNeeded(void)
1717 {
1718   if (game.sokoban_objects_still_needed > 0)
1719     game.sokoban_objects_still_needed--;
1720 }
1721
1722 static void InitPlayerField(int x, int y, int element, boolean init_game)
1723 {
1724   if (element == EL_SP_MURPHY)
1725   {
1726     if (init_game)
1727     {
1728       if (stored_player[0].present)
1729       {
1730         Tile[x][y] = EL_SP_MURPHY_CLONE;
1731
1732         return;
1733       }
1734       else
1735       {
1736         stored_player[0].initial_element = element;
1737         stored_player[0].use_murphy = TRUE;
1738
1739         if (!level.use_artwork_element[0])
1740           stored_player[0].artwork_element = EL_SP_MURPHY;
1741       }
1742
1743       Tile[x][y] = EL_PLAYER_1;
1744     }
1745   }
1746
1747   if (init_game)
1748   {
1749     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1750     int jx = player->jx, jy = player->jy;
1751
1752     player->present = TRUE;
1753
1754     player->block_last_field = (element == EL_SP_MURPHY ?
1755                                 level.sp_block_last_field :
1756                                 level.block_last_field);
1757
1758     // ---------- initialize player's last field block delay ------------------
1759
1760     // always start with reliable default value (no adjustment needed)
1761     player->block_delay_adjustment = 0;
1762
1763     // special case 1: in Supaplex, Murphy blocks last field one more frame
1764     if (player->block_last_field && element == EL_SP_MURPHY)
1765       player->block_delay_adjustment = 1;
1766
1767     // special case 2: in game engines before 3.1.1, blocking was different
1768     if (game.use_block_last_field_bug)
1769       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1770
1771     if (!network.enabled || player->connected_network)
1772     {
1773       player->active = TRUE;
1774
1775       // remove potentially duplicate players
1776       if (StorePlayer[jx][jy] == Tile[x][y])
1777         StorePlayer[jx][jy] = 0;
1778
1779       StorePlayer[x][y] = Tile[x][y];
1780
1781 #if DEBUG_INIT_PLAYER
1782       Debug("game:init:player", "- player element %d activated",
1783             player->element_nr);
1784       Debug("game:init:player", "  (local player is %d and currently %s)",
1785             local_player->element_nr,
1786             local_player->active ? "active" : "not active");
1787     }
1788 #endif
1789
1790     Tile[x][y] = EL_EMPTY;
1791
1792     player->jx = player->last_jx = x;
1793     player->jy = player->last_jy = y;
1794   }
1795
1796   // always check if player was just killed and should be reanimated
1797   {
1798     int player_nr = GET_PLAYER_NR(element);
1799     struct PlayerInfo *player = &stored_player[player_nr];
1800
1801     if (player->active && player->killed)
1802       player->reanimated = TRUE; // if player was just killed, reanimate him
1803   }
1804 }
1805
1806 static void InitField(int x, int y, boolean init_game)
1807 {
1808   int element = Tile[x][y];
1809
1810   switch (element)
1811   {
1812     case EL_SP_MURPHY:
1813     case EL_PLAYER_1:
1814     case EL_PLAYER_2:
1815     case EL_PLAYER_3:
1816     case EL_PLAYER_4:
1817       InitPlayerField(x, y, element, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_PLAYER:
1821       element = Tile[x][y] = EL_PLAYER_1;
1822       InitField(x, y, init_game);
1823
1824       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1825       InitField(x, y, init_game);
1826       break;
1827
1828     case EL_SOKOBAN_FIELD_EMPTY:
1829       IncrementSokobanFieldsNeeded();
1830       break;
1831
1832     case EL_SOKOBAN_OBJECT:
1833       IncrementSokobanObjectsNeeded();
1834       break;
1835
1836     case EL_STONEBLOCK:
1837       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1838         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1839       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1840         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1841       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1844         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1845       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1847       break;
1848
1849     case EL_BUG:
1850     case EL_BUG_RIGHT:
1851     case EL_BUG_UP:
1852     case EL_BUG_LEFT:
1853     case EL_BUG_DOWN:
1854     case EL_SPACESHIP:
1855     case EL_SPACESHIP_RIGHT:
1856     case EL_SPACESHIP_UP:
1857     case EL_SPACESHIP_LEFT:
1858     case EL_SPACESHIP_DOWN:
1859     case EL_BD_BUTTERFLY:
1860     case EL_BD_BUTTERFLY_RIGHT:
1861     case EL_BD_BUTTERFLY_UP:
1862     case EL_BD_BUTTERFLY_LEFT:
1863     case EL_BD_BUTTERFLY_DOWN:
1864     case EL_BD_FIREFLY:
1865     case EL_BD_FIREFLY_RIGHT:
1866     case EL_BD_FIREFLY_UP:
1867     case EL_BD_FIREFLY_LEFT:
1868     case EL_BD_FIREFLY_DOWN:
1869     case EL_PACMAN_RIGHT:
1870     case EL_PACMAN_UP:
1871     case EL_PACMAN_LEFT:
1872     case EL_PACMAN_DOWN:
1873     case EL_YAMYAM:
1874     case EL_YAMYAM_LEFT:
1875     case EL_YAMYAM_RIGHT:
1876     case EL_YAMYAM_UP:
1877     case EL_YAMYAM_DOWN:
1878     case EL_DARK_YAMYAM:
1879     case EL_ROBOT:
1880     case EL_PACMAN:
1881     case EL_SP_SNIKSNAK:
1882     case EL_SP_ELECTRON:
1883     case EL_MOLE:
1884     case EL_MOLE_LEFT:
1885     case EL_MOLE_RIGHT:
1886     case EL_MOLE_UP:
1887     case EL_MOLE_DOWN:
1888     case EL_SPRING_LEFT:
1889     case EL_SPRING_RIGHT:
1890       InitMovDir(x, y);
1891       break;
1892
1893     case EL_AMOEBA_FULL:
1894     case EL_BD_AMOEBA:
1895       InitAmoebaNr(x, y);
1896       break;
1897
1898     case EL_AMOEBA_DROP:
1899       if (y == lev_fieldy - 1)
1900       {
1901         Tile[x][y] = EL_AMOEBA_GROWING;
1902         Store[x][y] = EL_AMOEBA_WET;
1903       }
1904       break;
1905
1906     case EL_DYNAMITE_ACTIVE:
1907     case EL_SP_DISK_RED_ACTIVE:
1908     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1909     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1910     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1912       MovDelay[x][y] = 96;
1913       break;
1914
1915     case EL_EM_DYNAMITE_ACTIVE:
1916       MovDelay[x][y] = 32;
1917       break;
1918
1919     case EL_LAMP:
1920       game.lights_still_needed++;
1921       break;
1922
1923     case EL_PENGUIN:
1924       game.friends_still_needed++;
1925       break;
1926
1927     case EL_PIG:
1928     case EL_DRAGON:
1929       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1930       break;
1931
1932     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1933     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1934     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1935     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1944       if (init_game)
1945       {
1946         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1947         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1948         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1949
1950         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1951         {
1952           game.belt_dir[belt_nr] = belt_dir;
1953           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1954         }
1955         else    // more than one switch -- set it like the first switch
1956         {
1957           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1958         }
1959       }
1960       break;
1961
1962     case EL_LIGHT_SWITCH_ACTIVE:
1963       if (init_game)
1964         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1965       break;
1966
1967     case EL_INVISIBLE_STEELWALL:
1968     case EL_INVISIBLE_WALL:
1969     case EL_INVISIBLE_SAND:
1970       if (game.light_time_left > 0 ||
1971           game.lenses_time_left > 0)
1972         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1973       break;
1974
1975     case EL_EMC_MAGIC_BALL:
1976       if (game.ball_active)
1977         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1978       break;
1979
1980     case EL_EMC_MAGIC_BALL_SWITCH:
1981       if (game.ball_active)
1982         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1983       break;
1984
1985     case EL_TRIGGER_PLAYER:
1986     case EL_TRIGGER_ELEMENT:
1987     case EL_TRIGGER_CE_VALUE:
1988     case EL_TRIGGER_CE_SCORE:
1989     case EL_SELF:
1990     case EL_ANY_ELEMENT:
1991     case EL_CURRENT_CE_VALUE:
1992     case EL_CURRENT_CE_SCORE:
1993     case EL_PREV_CE_1:
1994     case EL_PREV_CE_2:
1995     case EL_PREV_CE_3:
1996     case EL_PREV_CE_4:
1997     case EL_PREV_CE_5:
1998     case EL_PREV_CE_6:
1999     case EL_PREV_CE_7:
2000     case EL_PREV_CE_8:
2001     case EL_NEXT_CE_1:
2002     case EL_NEXT_CE_2:
2003     case EL_NEXT_CE_3:
2004     case EL_NEXT_CE_4:
2005     case EL_NEXT_CE_5:
2006     case EL_NEXT_CE_6:
2007     case EL_NEXT_CE_7:
2008     case EL_NEXT_CE_8:
2009       // reference elements should not be used on the playfield
2010       Tile[x][y] = EL_EMPTY;
2011       break;
2012
2013     default:
2014       if (IS_CUSTOM_ELEMENT(element))
2015       {
2016         if (CAN_MOVE(element))
2017           InitMovDir(x, y);
2018
2019         if (!element_info[element].use_last_ce_value || init_game)
2020           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2021       }
2022       else if (IS_GROUP_ELEMENT(element))
2023       {
2024         Tile[x][y] = GetElementFromGroupElement(element);
2025
2026         InitField(x, y, init_game);
2027       }
2028
2029       break;
2030   }
2031
2032   if (!init_game)
2033     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2034 }
2035
2036 static void InitField_WithBug1(int x, int y, boolean init_game)
2037 {
2038   InitField(x, y, init_game);
2039
2040   // not needed to call InitMovDir() -- already done by InitField()!
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(Tile[x][y]))
2043     InitMovDir(x, y);
2044 }
2045
2046 static void InitField_WithBug2(int x, int y, boolean init_game)
2047 {
2048   int old_element = Tile[x][y];
2049
2050   InitField(x, y, init_game);
2051
2052   // not needed to call InitMovDir() -- already done by InitField()!
2053   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054       CAN_MOVE(old_element) &&
2055       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2056     InitMovDir(x, y);
2057
2058   /* this case is in fact a combination of not less than three bugs:
2059      first, it calls InitMovDir() for elements that can move, although this is
2060      already done by InitField(); then, it checks the element that was at this
2061      field _before_ the call to InitField() (which can change it); lastly, it
2062      was not called for "mole with direction" elements, which were treated as
2063      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2064   */
2065 }
2066
2067 static int get_key_element_from_nr(int key_nr)
2068 {
2069   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2070                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2071                           EL_EM_KEY_1 : EL_KEY_1);
2072
2073   return key_base_element + key_nr;
2074 }
2075
2076 static int get_next_dropped_element(struct PlayerInfo *player)
2077 {
2078   return (player->inventory_size > 0 ?
2079           player->inventory_element[player->inventory_size - 1] :
2080           player->inventory_infinite_element != EL_UNDEFINED ?
2081           player->inventory_infinite_element :
2082           player->dynabombs_left > 0 ?
2083           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2084           EL_UNDEFINED);
2085 }
2086
2087 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2088 {
2089   // pos >= 0: get element from bottom of the stack;
2090   // pos <  0: get element from top of the stack
2091
2092   if (pos < 0)
2093   {
2094     int min_inventory_size = -pos;
2095     int inventory_pos = player->inventory_size - min_inventory_size;
2096     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2097
2098     return (player->inventory_size >= min_inventory_size ?
2099             player->inventory_element[inventory_pos] :
2100             player->inventory_infinite_element != EL_UNDEFINED ?
2101             player->inventory_infinite_element :
2102             player->dynabombs_left >= min_dynabombs_left ?
2103             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2104             EL_UNDEFINED);
2105   }
2106   else
2107   {
2108     int min_dynabombs_left = pos + 1;
2109     int min_inventory_size = pos + 1 - player->dynabombs_left;
2110     int inventory_pos = pos - player->dynabombs_left;
2111
2112     return (player->inventory_infinite_element != EL_UNDEFINED ?
2113             player->inventory_infinite_element :
2114             player->dynabombs_left >= min_dynabombs_left ?
2115             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2116             player->inventory_size >= min_inventory_size ?
2117             player->inventory_element[inventory_pos] :
2118             EL_UNDEFINED);
2119   }
2120 }
2121
2122 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2123 {
2124   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2125   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2126   int compare_result;
2127
2128   if (gpo1->sort_priority != gpo2->sort_priority)
2129     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2130   else
2131     compare_result = gpo1->nr - gpo2->nr;
2132
2133   return compare_result;
2134 }
2135
2136 int getPlayerInventorySize(int player_nr)
2137 {
2138   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2139     return game_em.ply[player_nr]->dynamite;
2140   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2141     return game_sp.red_disk_count;
2142   else
2143     return stored_player[player_nr].inventory_size;
2144 }
2145
2146 static void InitGameControlValues(void)
2147 {
2148   int i;
2149
2150   for (i = 0; game_panel_controls[i].nr != -1; i++)
2151   {
2152     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2153     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2154     struct TextPosInfo *pos = gpc->pos;
2155     int nr = gpc->nr;
2156     int type = gpc->type;
2157
2158     if (nr != i)
2159     {
2160       Error("'game_panel_controls' structure corrupted at %d", i);
2161
2162       Fail("this should not happen -- please debug");
2163     }
2164
2165     // force update of game controls after initialization
2166     gpc->value = gpc->last_value = -1;
2167     gpc->frame = gpc->last_frame = -1;
2168     gpc->gfx_frame = -1;
2169
2170     // determine panel value width for later calculation of alignment
2171     if (type == TYPE_INTEGER || type == TYPE_STRING)
2172     {
2173       pos->width = pos->size * getFontWidth(pos->font);
2174       pos->height = getFontHeight(pos->font);
2175     }
2176     else if (type == TYPE_ELEMENT)
2177     {
2178       pos->width = pos->size;
2179       pos->height = pos->size;
2180     }
2181
2182     // fill structure for game panel draw order
2183     gpo->nr = gpc->nr;
2184     gpo->sort_priority = pos->sort_priority;
2185   }
2186
2187   // sort game panel controls according to sort_priority and control number
2188   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2189         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2190 }
2191
2192 static void UpdatePlayfieldElementCount(void)
2193 {
2194   boolean use_element_count = FALSE;
2195   int i, j, x, y;
2196
2197   // first check if it is needed at all to calculate playfield element count
2198   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2199     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2200       use_element_count = TRUE;
2201
2202   if (!use_element_count)
2203     return;
2204
2205   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2206     element_info[i].element_count = 0;
2207
2208   SCAN_PLAYFIELD(x, y)
2209   {
2210     element_info[Tile[x][y]].element_count++;
2211   }
2212
2213   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2214     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2215       if (IS_IN_GROUP(j, i))
2216         element_info[EL_GROUP_START + i].element_count +=
2217           element_info[j].element_count;
2218 }
2219
2220 static void UpdateGameControlValues(void)
2221 {
2222   int i, k;
2223   int time = (game.LevelSolved ?
2224               game.LevelSolved_CountingTime :
2225               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2226               game_em.lev->time :
2227               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2228               game_sp.time_played :
2229               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2230               game_mm.energy_left :
2231               game.no_time_limit ? TimePlayed : TimeLeft);
2232   int score = (game.LevelSolved ?
2233                game.LevelSolved_CountingScore :
2234                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2235                game_em.lev->score :
2236                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2237                game_sp.score :
2238                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2239                game_mm.score :
2240                game.score);
2241   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2242               game_em.lev->gems_needed :
2243               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2244               game_sp.infotrons_still_needed :
2245               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2246               game_mm.kettles_still_needed :
2247               game.gems_still_needed);
2248   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2249                      game_em.lev->gems_needed > 0 :
2250                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2251                      game_sp.infotrons_still_needed > 0 :
2252                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2253                      game_mm.kettles_still_needed > 0 ||
2254                      game_mm.lights_still_needed > 0 :
2255                      game.gems_still_needed > 0 ||
2256                      game.sokoban_fields_still_needed > 0 ||
2257                      game.sokoban_objects_still_needed > 0 ||
2258                      game.lights_still_needed > 0);
2259   int health = (game.LevelSolved ?
2260                 game.LevelSolved_CountingHealth :
2261                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2262                 MM_HEALTH(game_mm.laser_overload_value) :
2263                 game.health);
2264   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2265
2266   UpdatePlayfieldElementCount();
2267
2268   // update game panel control values
2269
2270   // used instead of "level_nr" (for network games)
2271   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2272   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2273
2274   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2275   for (i = 0; i < MAX_NUM_KEYS; i++)
2276     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2277   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2278   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2279
2280   if (game.centered_player_nr == -1)
2281   {
2282     for (i = 0; i < MAX_PLAYERS; i++)
2283     {
2284       // only one player in Supaplex game engine
2285       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2286         break;
2287
2288       for (k = 0; k < MAX_NUM_KEYS; k++)
2289       {
2290         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2291         {
2292           if (game_em.ply[i]->keys & (1 << k))
2293             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294               get_key_element_from_nr(k);
2295         }
2296         else if (stored_player[i].key[k])
2297           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2298             get_key_element_from_nr(k);
2299       }
2300
2301       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2302         getPlayerInventorySize(i);
2303
2304       if (stored_player[i].num_white_keys > 0)
2305         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2306           EL_DC_KEY_WHITE;
2307
2308       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2309         stored_player[i].num_white_keys;
2310     }
2311   }
2312   else
2313   {
2314     int player_nr = game.centered_player_nr;
2315
2316     for (k = 0; k < MAX_NUM_KEYS; k++)
2317     {
2318       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2319       {
2320         if (game_em.ply[player_nr]->keys & (1 << k))
2321           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2322             get_key_element_from_nr(k);
2323       }
2324       else if (stored_player[player_nr].key[k])
2325         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2326           get_key_element_from_nr(k);
2327     }
2328
2329     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2330       getPlayerInventorySize(player_nr);
2331
2332     if (stored_player[player_nr].num_white_keys > 0)
2333       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2334
2335     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2336       stored_player[player_nr].num_white_keys;
2337   }
2338
2339   // re-arrange keys on game panel, if needed or if defined by style settings
2340   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2341   {
2342     int nr = GAME_PANEL_KEY_1 + i;
2343     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2344     struct TextPosInfo *pos = gpc->pos;
2345
2346     // skip check if key is not in the player's inventory
2347     if (gpc->value == EL_EMPTY)
2348       continue;
2349
2350     // check if keys should be arranged on panel from left to right
2351     if (pos->style == STYLE_LEFTMOST_POSITION)
2352     {
2353       // check previous key positions (left from current key)
2354       for (k = 0; k < i; k++)
2355       {
2356         int nr_new = GAME_PANEL_KEY_1 + k;
2357
2358         if (game_panel_controls[nr_new].value == EL_EMPTY)
2359         {
2360           game_panel_controls[nr_new].value = gpc->value;
2361           gpc->value = EL_EMPTY;
2362
2363           break;
2364         }
2365       }
2366     }
2367
2368     // check if "undefined" keys can be placed at some other position
2369     if (pos->x == -1 && pos->y == -1)
2370     {
2371       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2372
2373       // 1st try: display key at the same position as normal or EM keys
2374       if (game_panel_controls[nr_new].value == EL_EMPTY)
2375       {
2376         game_panel_controls[nr_new].value = gpc->value;
2377       }
2378       else
2379       {
2380         // 2nd try: display key at the next free position in the key panel
2381         for (k = 0; k < STD_NUM_KEYS; k++)
2382         {
2383           nr_new = GAME_PANEL_KEY_1 + k;
2384
2385           if (game_panel_controls[nr_new].value == EL_EMPTY)
2386           {
2387             game_panel_controls[nr_new].value = gpc->value;
2388
2389             break;
2390           }
2391         }
2392       }
2393     }
2394   }
2395
2396   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2397   {
2398     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2399       get_inventory_element_from_pos(local_player, i);
2400     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2401       get_inventory_element_from_pos(local_player, -i - 1);
2402   }
2403
2404   game_panel_controls[GAME_PANEL_SCORE].value = score;
2405   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2406
2407   game_panel_controls[GAME_PANEL_TIME].value = time;
2408
2409   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2410   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2411   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2412
2413   if (level.time == 0)
2414     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2415   else
2416     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2417
2418   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2419   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2420
2421   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2422
2423   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2424     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2425      EL_EMPTY);
2426   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2427     local_player->shield_normal_time_left;
2428   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2429     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2430      EL_EMPTY);
2431   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2432     local_player->shield_deadly_time_left;
2433
2434   game_panel_controls[GAME_PANEL_EXIT].value =
2435     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2436
2437   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2438     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2439   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2440     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2441      EL_EMC_MAGIC_BALL_SWITCH);
2442
2443   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2444     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2445   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2446     game.light_time_left;
2447
2448   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2449     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2450   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2451     game.timegate_time_left;
2452
2453   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2454     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2455
2456   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2457     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2458   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2459     game.lenses_time_left;
2460
2461   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2462     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2463   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2464     game.magnify_time_left;
2465
2466   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2467     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2468      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2469      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2470      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2471      EL_BALLOON_SWITCH_NONE);
2472
2473   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2474     local_player->dynabomb_count;
2475   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2476     local_player->dynabomb_size;
2477   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2478     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2479
2480   game_panel_controls[GAME_PANEL_PENGUINS].value =
2481     game.friends_still_needed;
2482
2483   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2484     game.sokoban_objects_still_needed;
2485   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2486     game.sokoban_fields_still_needed;
2487
2488   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2489     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2490
2491   for (i = 0; i < NUM_BELTS; i++)
2492   {
2493     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2494       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2495        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2496     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2497       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2498   }
2499
2500   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2501     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2502   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2503     game.magic_wall_time_left;
2504
2505   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2506     local_player->gravity;
2507
2508   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2509     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2510
2511   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2512     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2513       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2514        game.panel.element[i].id : EL_UNDEFINED);
2515
2516   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2517     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2518       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2519        element_info[game.panel.element_count[i].id].element_count : 0);
2520
2521   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2522     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2523       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2524        element_info[game.panel.ce_score[i].id].collect_score : 0);
2525
2526   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2527     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2528       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2529        element_info[game.panel.ce_score_element[i].id].collect_score :
2530        EL_UNDEFINED);
2531
2532   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2533   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2534   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2535
2536   // update game panel control frames
2537
2538   for (i = 0; game_panel_controls[i].nr != -1; i++)
2539   {
2540     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2541
2542     if (gpc->type == TYPE_ELEMENT)
2543     {
2544       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2545       {
2546         int last_anim_random_frame = gfx.anim_random_frame;
2547         int element = gpc->value;
2548         int graphic = el2panelimg(element);
2549         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2550                                sync_random_frame : INIT_GFX_RANDOM());
2551
2552         if (gpc->value != gpc->last_value)
2553         {
2554           gpc->gfx_frame = 0;
2555           gpc->gfx_random = init_gfx_random;
2556         }
2557         else
2558         {
2559           gpc->gfx_frame++;
2560
2561           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2562               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2563             gpc->gfx_random = init_gfx_random;
2564         }
2565
2566         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2567           gfx.anim_random_frame = gpc->gfx_random;
2568
2569         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2570           gpc->gfx_frame = element_info[element].collect_score;
2571
2572         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2573
2574         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2575           gfx.anim_random_frame = last_anim_random_frame;
2576       }
2577     }
2578     else if (gpc->type == TYPE_GRAPHIC)
2579     {
2580       if (gpc->graphic != IMG_UNDEFINED)
2581       {
2582         int last_anim_random_frame = gfx.anim_random_frame;
2583         int graphic = gpc->graphic;
2584         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2585                                sync_random_frame : INIT_GFX_RANDOM());
2586
2587         if (gpc->value != gpc->last_value)
2588         {
2589           gpc->gfx_frame = 0;
2590           gpc->gfx_random = init_gfx_random;
2591         }
2592         else
2593         {
2594           gpc->gfx_frame++;
2595
2596           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2597               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2598             gpc->gfx_random = init_gfx_random;
2599         }
2600
2601         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2602           gfx.anim_random_frame = gpc->gfx_random;
2603
2604         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2605
2606         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2607           gfx.anim_random_frame = last_anim_random_frame;
2608       }
2609     }
2610   }
2611 }
2612
2613 static void DisplayGameControlValues(void)
2614 {
2615   boolean redraw_panel = FALSE;
2616   int i;
2617
2618   for (i = 0; game_panel_controls[i].nr != -1; i++)
2619   {
2620     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2621
2622     if (PANEL_DEACTIVATED(gpc->pos))
2623       continue;
2624
2625     if (gpc->value == gpc->last_value &&
2626         gpc->frame == gpc->last_frame)
2627       continue;
2628
2629     redraw_panel = TRUE;
2630   }
2631
2632   if (!redraw_panel)
2633     return;
2634
2635   // copy default game door content to main double buffer
2636
2637   // !!! CHECK AGAIN !!!
2638   SetPanelBackground();
2639   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2640   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2641
2642   // redraw game control buttons
2643   RedrawGameButtons();
2644
2645   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2646
2647   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2648   {
2649     int nr = game_panel_order[i].nr;
2650     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2651     struct TextPosInfo *pos = gpc->pos;
2652     int type = gpc->type;
2653     int value = gpc->value;
2654     int frame = gpc->frame;
2655     int size = pos->size;
2656     int font = pos->font;
2657     boolean draw_masked = pos->draw_masked;
2658     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2659
2660     if (PANEL_DEACTIVATED(pos))
2661       continue;
2662
2663     if (pos->class == get_hash_from_key("extra_panel_items") &&
2664         !setup.prefer_extra_panel_items)
2665       continue;
2666
2667     gpc->last_value = value;
2668     gpc->last_frame = frame;
2669
2670     if (type == TYPE_INTEGER)
2671     {
2672       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2673           nr == GAME_PANEL_TIME)
2674       {
2675         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2676
2677         if (use_dynamic_size)           // use dynamic number of digits
2678         {
2679           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2680           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2681           int size2 = size1 + 1;
2682           int font1 = pos->font;
2683           int font2 = pos->font_alt;
2684
2685           size = (value < value_change ? size1 : size2);
2686           font = (value < value_change ? font1 : font2);
2687         }
2688       }
2689
2690       // correct text size if "digits" is zero or less
2691       if (size <= 0)
2692         size = strlen(int2str(value, size));
2693
2694       // dynamically correct text alignment
2695       pos->width = size * getFontWidth(font);
2696
2697       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2698                   int2str(value, size), font, mask_mode);
2699     }
2700     else if (type == TYPE_ELEMENT)
2701     {
2702       int element, graphic;
2703       Bitmap *src_bitmap;
2704       int src_x, src_y;
2705       int width, height;
2706       int dst_x = PANEL_XPOS(pos);
2707       int dst_y = PANEL_YPOS(pos);
2708
2709       if (value != EL_UNDEFINED && value != EL_EMPTY)
2710       {
2711         element = value;
2712         graphic = el2panelimg(value);
2713
2714 #if 0
2715         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2716               element, EL_NAME(element), size);
2717 #endif
2718
2719         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2720           size = TILESIZE;
2721
2722         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2723                               &src_x, &src_y);
2724
2725         width  = graphic_info[graphic].width  * size / TILESIZE;
2726         height = graphic_info[graphic].height * size / TILESIZE;
2727
2728         if (draw_masked)
2729           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2730                            dst_x, dst_y);
2731         else
2732           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2733                      dst_x, dst_y);
2734       }
2735     }
2736     else if (type == TYPE_GRAPHIC)
2737     {
2738       int graphic        = gpc->graphic;
2739       int graphic_active = gpc->graphic_active;
2740       Bitmap *src_bitmap;
2741       int src_x, src_y;
2742       int width, height;
2743       int dst_x = PANEL_XPOS(pos);
2744       int dst_y = PANEL_YPOS(pos);
2745       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2746                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2747
2748       if (graphic != IMG_UNDEFINED && !skip)
2749       {
2750         if (pos->style == STYLE_REVERSE)
2751           value = 100 - value;
2752
2753         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2754
2755         if (pos->direction & MV_HORIZONTAL)
2756         {
2757           width  = graphic_info[graphic_active].width * value / 100;
2758           height = graphic_info[graphic_active].height;
2759
2760           if (pos->direction == MV_LEFT)
2761           {
2762             src_x += graphic_info[graphic_active].width - width;
2763             dst_x += graphic_info[graphic_active].width - width;
2764           }
2765         }
2766         else
2767         {
2768           width  = graphic_info[graphic_active].width;
2769           height = graphic_info[graphic_active].height * value / 100;
2770
2771           if (pos->direction == MV_UP)
2772           {
2773             src_y += graphic_info[graphic_active].height - height;
2774             dst_y += graphic_info[graphic_active].height - height;
2775           }
2776         }
2777
2778         if (draw_masked)
2779           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2780                            dst_x, dst_y);
2781         else
2782           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2783                      dst_x, dst_y);
2784
2785         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2786
2787         if (pos->direction & MV_HORIZONTAL)
2788         {
2789           if (pos->direction == MV_RIGHT)
2790           {
2791             src_x += width;
2792             dst_x += width;
2793           }
2794           else
2795           {
2796             dst_x = PANEL_XPOS(pos);
2797           }
2798
2799           width = graphic_info[graphic].width - width;
2800         }
2801         else
2802         {
2803           if (pos->direction == MV_DOWN)
2804           {
2805             src_y += height;
2806             dst_y += height;
2807           }
2808           else
2809           {
2810             dst_y = PANEL_YPOS(pos);
2811           }
2812
2813           height = graphic_info[graphic].height - height;
2814         }
2815
2816         if (draw_masked)
2817           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2818                            dst_x, dst_y);
2819         else
2820           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2821                      dst_x, dst_y);
2822       }
2823     }
2824     else if (type == TYPE_STRING)
2825     {
2826       boolean active = (value != 0);
2827       char *state_normal = "off";
2828       char *state_active = "on";
2829       char *state = (active ? state_active : state_normal);
2830       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2831                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2832                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2833                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2834
2835       if (nr == GAME_PANEL_GRAVITY_STATE)
2836       {
2837         int font1 = pos->font;          // (used for normal state)
2838         int font2 = pos->font_alt;      // (used for active state)
2839
2840         font = (active ? font2 : font1);
2841       }
2842
2843       if (s != NULL)
2844       {
2845         char *s_cut;
2846
2847         if (size <= 0)
2848         {
2849           // don't truncate output if "chars" is zero or less
2850           size = strlen(s);
2851
2852           // dynamically correct text alignment
2853           pos->width = size * getFontWidth(font);
2854         }
2855
2856         s_cut = getStringCopyN(s, size);
2857
2858         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2859                     s_cut, font, mask_mode);
2860
2861         free(s_cut);
2862       }
2863     }
2864
2865     redraw_mask |= REDRAW_DOOR_1;
2866   }
2867
2868   SetGameStatus(GAME_MODE_PLAYING);
2869 }
2870
2871 void UpdateAndDisplayGameControlValues(void)
2872 {
2873   if (tape.deactivate_display)
2874     return;
2875
2876   UpdateGameControlValues();
2877   DisplayGameControlValues();
2878 }
2879
2880 void UpdateGameDoorValues(void)
2881 {
2882   UpdateGameControlValues();
2883 }
2884
2885 void DrawGameDoorValues(void)
2886 {
2887   DisplayGameControlValues();
2888 }
2889
2890
2891 // ============================================================================
2892 // InitGameEngine()
2893 // ----------------------------------------------------------------------------
2894 // initialize game engine due to level / tape version number
2895 // ============================================================================
2896
2897 static void InitGameEngine(void)
2898 {
2899   int i, j, k, l, x, y;
2900
2901   // set game engine from tape file when re-playing, else from level file
2902   game.engine_version = (tape.playing ? tape.engine_version :
2903                          level.game_version);
2904
2905   // set single or multi-player game mode (needed for re-playing tapes)
2906   game.team_mode = setup.team_mode;
2907
2908   if (tape.playing)
2909   {
2910     int num_players = 0;
2911
2912     for (i = 0; i < MAX_PLAYERS; i++)
2913       if (tape.player_participates[i])
2914         num_players++;
2915
2916     // multi-player tapes contain input data for more than one player
2917     game.team_mode = (num_players > 1);
2918   }
2919
2920 #if 0
2921   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2922         level.game_version);
2923   Debug("game:init:level", "          tape.file_version   == %06d",
2924         tape.file_version);
2925   Debug("game:init:level", "          tape.game_version   == %06d",
2926         tape.game_version);
2927   Debug("game:init:level", "          tape.engine_version == %06d",
2928         tape.engine_version);
2929   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2930         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2931 #endif
2932
2933   // --------------------------------------------------------------------------
2934   // set flags for bugs and changes according to active game engine version
2935   // --------------------------------------------------------------------------
2936
2937   /*
2938     Summary of bugfix:
2939     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2940
2941     Bug was introduced in version:
2942     2.0.1
2943
2944     Bug was fixed in version:
2945     4.2.0.0
2946
2947     Description:
2948     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2949     but the property "can fall" was missing, which caused some levels to be
2950     unsolvable. This was fixed in version 4.2.0.0.
2951
2952     Affected levels/tapes:
2953     An example for a tape that was fixed by this bugfix is tape 029 from the
2954     level set "rnd_sam_bateman".
2955     The wrong behaviour will still be used for all levels or tapes that were
2956     created/recorded with it. An example for this is tape 023 from the level
2957     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2958   */
2959
2960   boolean use_amoeba_dropping_cannot_fall_bug =
2961     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2962       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2963      (tape.playing &&
2964       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2965       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2966
2967   /*
2968     Summary of bugfix/change:
2969     Fixed move speed of elements entering or leaving magic wall.
2970
2971     Fixed/changed in version:
2972     2.0.1
2973
2974     Description:
2975     Before 2.0.1, move speed of elements entering or leaving magic wall was
2976     twice as fast as it is now.
2977     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2978
2979     Affected levels/tapes:
2980     The first condition is generally needed for all levels/tapes before version
2981     2.0.1, which might use the old behaviour before it was changed; known tapes
2982     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2983     The second condition is an exception from the above case and is needed for
2984     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2985     above, but before it was known that this change would break tapes like the
2986     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2987     although the engine version while recording maybe was before 2.0.1. There
2988     are a lot of tapes that are affected by this exception, like tape 006 from
2989     the level set "rnd_conor_mancone".
2990   */
2991
2992   boolean use_old_move_stepsize_for_magic_wall =
2993     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2994      !(tape.playing &&
2995        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2996        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2997
2998   /*
2999     Summary of bugfix/change:
3000     Fixed handling for custom elements that change when pushed by the player.
3001
3002     Fixed/changed in version:
3003     3.1.0
3004
3005     Description:
3006     Before 3.1.0, custom elements that "change when pushing" changed directly
3007     after the player started pushing them (until then handled in "DigField()").
3008     Since 3.1.0, these custom elements are not changed until the "pushing"
3009     move of the element is finished (now handled in "ContinueMoving()").
3010
3011     Affected levels/tapes:
3012     The first condition is generally needed for all levels/tapes before version
3013     3.1.0, which might use the old behaviour before it was changed; known tapes
3014     that are affected are some tapes from the level set "Walpurgis Gardens" by
3015     Jamie Cullen.
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3018     above (including some development versions of 3.1.0), but before it was
3019     known that this change would break tapes like the above and was fixed in
3020     3.1.1, so that the changed behaviour was active although the engine version
3021     while recording maybe was before 3.1.0. There is at least one tape that is
3022     affected by this exception, which is the tape for the one-level set "Bug
3023     Machine" by Juergen Bonhagen.
3024   */
3025
3026   game.use_change_when_pushing_bug =
3027     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3028      !(tape.playing &&
3029        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3030        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3031
3032   /*
3033     Summary of bugfix/change:
3034     Fixed handling for blocking the field the player leaves when moving.
3035
3036     Fixed/changed in version:
3037     3.1.1
3038
3039     Description:
3040     Before 3.1.1, when "block last field when moving" was enabled, the field
3041     the player is leaving when moving was blocked for the time of the move,
3042     and was directly unblocked afterwards. This resulted in the last field
3043     being blocked for exactly one less than the number of frames of one player
3044     move. Additionally, even when blocking was disabled, the last field was
3045     blocked for exactly one frame.
3046     Since 3.1.1, due to changes in player movement handling, the last field
3047     is not blocked at all when blocking is disabled. When blocking is enabled,
3048     the last field is blocked for exactly the number of frames of one player
3049     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3050     last field is blocked for exactly one more than the number of frames of
3051     one player move.
3052
3053     Affected levels/tapes:
3054     (!!! yet to be determined -- probably many !!!)
3055   */
3056
3057   game.use_block_last_field_bug =
3058     (game.engine_version < VERSION_IDENT(3,1,1,0));
3059
3060   /* various special flags and settings for native Emerald Mine game engine */
3061
3062   game_em.use_single_button =
3063     (game.engine_version > VERSION_IDENT(4,0,0,2));
3064
3065   game_em.use_snap_key_bug =
3066     (game.engine_version < VERSION_IDENT(4,0,1,0));
3067
3068   game_em.use_random_bug =
3069     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3070
3071   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3072
3073   game_em.use_old_explosions            = use_old_em_engine;
3074   game_em.use_old_android               = use_old_em_engine;
3075   game_em.use_old_push_elements         = use_old_em_engine;
3076   game_em.use_old_push_into_acid        = use_old_em_engine;
3077
3078   game_em.use_wrap_around               = !use_old_em_engine;
3079
3080   // --------------------------------------------------------------------------
3081
3082   // set maximal allowed number of custom element changes per game frame
3083   game.max_num_changes_per_frame = 1;
3084
3085   // default scan direction: scan playfield from top/left to bottom/right
3086   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3087
3088   // dynamically adjust element properties according to game engine version
3089   InitElementPropertiesEngine(game.engine_version);
3090
3091   // ---------- initialize special element properties -------------------------
3092
3093   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3094   if (use_amoeba_dropping_cannot_fall_bug)
3095     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3096
3097   // ---------- initialize player's initial move delay ------------------------
3098
3099   // dynamically adjust player properties according to level information
3100   for (i = 0; i < MAX_PLAYERS; i++)
3101     game.initial_move_delay_value[i] =
3102       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3103
3104   // dynamically adjust player properties according to game engine version
3105   for (i = 0; i < MAX_PLAYERS; i++)
3106     game.initial_move_delay[i] =
3107       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3108        game.initial_move_delay_value[i] : 0);
3109
3110   // ---------- initialize player's initial push delay ------------------------
3111
3112   // dynamically adjust player properties according to game engine version
3113   game.initial_push_delay_value =
3114     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3115
3116   // ---------- initialize changing elements ----------------------------------
3117
3118   // initialize changing elements information
3119   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3120   {
3121     struct ElementInfo *ei = &element_info[i];
3122
3123     // this pointer might have been changed in the level editor
3124     ei->change = &ei->change_page[0];
3125
3126     if (!IS_CUSTOM_ELEMENT(i))
3127     {
3128       ei->change->target_element = EL_EMPTY_SPACE;
3129       ei->change->delay_fixed = 0;
3130       ei->change->delay_random = 0;
3131       ei->change->delay_frames = 1;
3132     }
3133
3134     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3135     {
3136       ei->has_change_event[j] = FALSE;
3137
3138       ei->event_page_nr[j] = 0;
3139       ei->event_page[j] = &ei->change_page[0];
3140     }
3141   }
3142
3143   // add changing elements from pre-defined list
3144   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3145   {
3146     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3147     struct ElementInfo *ei = &element_info[ch_delay->element];
3148
3149     ei->change->target_element       = ch_delay->target_element;
3150     ei->change->delay_fixed          = ch_delay->change_delay;
3151
3152     ei->change->pre_change_function  = ch_delay->pre_change_function;
3153     ei->change->change_function      = ch_delay->change_function;
3154     ei->change->post_change_function = ch_delay->post_change_function;
3155
3156     ei->change->can_change = TRUE;
3157     ei->change->can_change_or_has_action = TRUE;
3158
3159     ei->has_change_event[CE_DELAY] = TRUE;
3160
3161     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3162     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3163   }
3164
3165   // ---------- initialize internal run-time variables ------------------------
3166
3167   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3168   {
3169     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3170
3171     for (j = 0; j < ei->num_change_pages; j++)
3172     {
3173       ei->change_page[j].can_change_or_has_action =
3174         (ei->change_page[j].can_change |
3175          ei->change_page[j].has_action);
3176     }
3177   }
3178
3179   // add change events from custom element configuration
3180   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3181   {
3182     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3183
3184     for (j = 0; j < ei->num_change_pages; j++)
3185     {
3186       if (!ei->change_page[j].can_change_or_has_action)
3187         continue;
3188
3189       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3190       {
3191         // only add event page for the first page found with this event
3192         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3193         {
3194           ei->has_change_event[k] = TRUE;
3195
3196           ei->event_page_nr[k] = j;
3197           ei->event_page[k] = &ei->change_page[j];
3198         }
3199       }
3200     }
3201   }
3202
3203   // ---------- initialize reference elements in change conditions ------------
3204
3205   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3206   {
3207     int element = EL_CUSTOM_START + i;
3208     struct ElementInfo *ei = &element_info[element];
3209
3210     for (j = 0; j < ei->num_change_pages; j++)
3211     {
3212       int trigger_element = ei->change_page[j].initial_trigger_element;
3213
3214       if (trigger_element >= EL_PREV_CE_8 &&
3215           trigger_element <= EL_NEXT_CE_8)
3216         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3217
3218       ei->change_page[j].trigger_element = trigger_element;
3219     }
3220   }
3221
3222   // ---------- initialize run-time trigger player and element ----------------
3223
3224   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3225   {
3226     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3227
3228     for (j = 0; j < ei->num_change_pages; j++)
3229     {
3230       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3231       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3232       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3233       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3234       ei->change_page[j].actual_trigger_ce_value = 0;
3235       ei->change_page[j].actual_trigger_ce_score = 0;
3236     }
3237   }
3238
3239   // ---------- initialize trigger events -------------------------------------
3240
3241   // initialize trigger events information
3242   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3243     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3244       trigger_events[i][j] = FALSE;
3245
3246   // add trigger events from element change event properties
3247   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3248   {
3249     struct ElementInfo *ei = &element_info[i];
3250
3251     for (j = 0; j < ei->num_change_pages; j++)
3252     {
3253       if (!ei->change_page[j].can_change_or_has_action)
3254         continue;
3255
3256       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3257       {
3258         int trigger_element = ei->change_page[j].trigger_element;
3259
3260         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3261         {
3262           if (ei->change_page[j].has_event[k])
3263           {
3264             if (IS_GROUP_ELEMENT(trigger_element))
3265             {
3266               struct ElementGroupInfo *group =
3267                 element_info[trigger_element].group;
3268
3269               for (l = 0; l < group->num_elements_resolved; l++)
3270                 trigger_events[group->element_resolved[l]][k] = TRUE;
3271             }
3272             else if (trigger_element == EL_ANY_ELEMENT)
3273               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3274                 trigger_events[l][k] = TRUE;
3275             else
3276               trigger_events[trigger_element][k] = TRUE;
3277           }
3278         }
3279       }
3280     }
3281   }
3282
3283   // ---------- initialize push delay -----------------------------------------
3284
3285   // initialize push delay values to default
3286   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3287   {
3288     if (!IS_CUSTOM_ELEMENT(i))
3289     {
3290       // set default push delay values (corrected since version 3.0.7-1)
3291       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3292       {
3293         element_info[i].push_delay_fixed = 2;
3294         element_info[i].push_delay_random = 8;
3295       }
3296       else
3297       {
3298         element_info[i].push_delay_fixed = 8;
3299         element_info[i].push_delay_random = 8;
3300       }
3301     }
3302   }
3303
3304   // set push delay value for certain elements from pre-defined list
3305   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3306   {
3307     int e = push_delay_list[i].element;
3308
3309     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3310     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3311   }
3312
3313   // set push delay value for Supaplex elements for newer engine versions
3314   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3315   {
3316     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3317     {
3318       if (IS_SP_ELEMENT(i))
3319       {
3320         // set SP push delay to just enough to push under a falling zonk
3321         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3322
3323         element_info[i].push_delay_fixed  = delay;
3324         element_info[i].push_delay_random = 0;
3325       }
3326     }
3327   }
3328
3329   // ---------- initialize move stepsize --------------------------------------
3330
3331   // initialize move stepsize values to default
3332   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3333     if (!IS_CUSTOM_ELEMENT(i))
3334       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3335
3336   // set move stepsize value for certain elements from pre-defined list
3337   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3338   {
3339     int e = move_stepsize_list[i].element;
3340
3341     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3342
3343     // set move stepsize value for certain elements for older engine versions
3344     if (use_old_move_stepsize_for_magic_wall)
3345     {
3346       if (e == EL_MAGIC_WALL_FILLING ||
3347           e == EL_MAGIC_WALL_EMPTYING ||
3348           e == EL_BD_MAGIC_WALL_FILLING ||
3349           e == EL_BD_MAGIC_WALL_EMPTYING)
3350         element_info[e].move_stepsize *= 2;
3351     }
3352   }
3353
3354   // ---------- initialize collect score --------------------------------------
3355
3356   // initialize collect score values for custom elements from initial value
3357   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3358     if (IS_CUSTOM_ELEMENT(i))
3359       element_info[i].collect_score = element_info[i].collect_score_initial;
3360
3361   // ---------- initialize collect count --------------------------------------
3362
3363   // initialize collect count values for non-custom elements
3364   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3365     if (!IS_CUSTOM_ELEMENT(i))
3366       element_info[i].collect_count_initial = 0;
3367
3368   // add collect count values for all elements from pre-defined list
3369   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3370     element_info[collect_count_list[i].element].collect_count_initial =
3371       collect_count_list[i].count;
3372
3373   // ---------- initialize access direction -----------------------------------
3374
3375   // initialize access direction values to default (access from every side)
3376   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3377     if (!IS_CUSTOM_ELEMENT(i))
3378       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3379
3380   // set access direction value for certain elements from pre-defined list
3381   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3382     element_info[access_direction_list[i].element].access_direction =
3383       access_direction_list[i].direction;
3384
3385   // ---------- initialize explosion content ----------------------------------
3386   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3387   {
3388     if (IS_CUSTOM_ELEMENT(i))
3389       continue;
3390
3391     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3392     {
3393       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3394
3395       element_info[i].content.e[x][y] =
3396         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3397          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3398          i == EL_PLAYER_3 ? EL_EMERALD :
3399          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3400          i == EL_MOLE ? EL_EMERALD_RED :
3401          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3402          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3403          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3404          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3405          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3406          i == EL_WALL_EMERALD ? EL_EMERALD :
3407          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3408          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3409          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3410          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3411          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3412          i == EL_WALL_PEARL ? EL_PEARL :
3413          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3414          EL_EMPTY);
3415     }
3416   }
3417
3418   // ---------- initialize recursion detection --------------------------------
3419   recursion_loop_depth = 0;
3420   recursion_loop_detected = FALSE;
3421   recursion_loop_element = EL_UNDEFINED;
3422
3423   // ---------- initialize graphics engine ------------------------------------
3424   game.scroll_delay_value =
3425     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3426      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3427      !setup.forced_scroll_delay           ? 0 :
3428      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3429   game.scroll_delay_value =
3430     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3431
3432   // ---------- initialize game engine snapshots ------------------------------
3433   for (i = 0; i < MAX_PLAYERS; i++)
3434     game.snapshot.last_action[i] = 0;
3435   game.snapshot.changed_action = FALSE;
3436   game.snapshot.collected_item = FALSE;
3437   game.snapshot.mode =
3438     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3439      SNAPSHOT_MODE_EVERY_STEP :
3440      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3441      SNAPSHOT_MODE_EVERY_MOVE :
3442      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3443      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3444   game.snapshot.save_snapshot = FALSE;
3445
3446   // ---------- initialize level time for Supaplex engine ---------------------
3447   // Supaplex levels with time limit currently unsupported -- should be added
3448   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3449     level.time = 0;
3450
3451   // ---------- initialize flags for handling game actions --------------------
3452
3453   // set flags for game actions to default values
3454   game.use_key_actions = TRUE;
3455   game.use_mouse_actions = FALSE;
3456
3457   // when using Mirror Magic game engine, handle mouse events only
3458   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3459   {
3460     game.use_key_actions = FALSE;
3461     game.use_mouse_actions = TRUE;
3462   }
3463
3464   // check for custom elements with mouse click events
3465   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3466   {
3467     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3468     {
3469       int element = EL_CUSTOM_START + i;
3470
3471       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3472           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3473           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3474           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3475         game.use_mouse_actions = TRUE;
3476     }
3477   }
3478 }
3479
3480 static int get_num_special_action(int element, int action_first,
3481                                   int action_last)
3482 {
3483   int num_special_action = 0;
3484   int i, j;
3485
3486   for (i = action_first; i <= action_last; i++)
3487   {
3488     boolean found = FALSE;
3489
3490     for (j = 0; j < NUM_DIRECTIONS; j++)
3491       if (el_act_dir2img(element, i, j) !=
3492           el_act_dir2img(element, ACTION_DEFAULT, j))
3493         found = TRUE;
3494
3495     if (found)
3496       num_special_action++;
3497     else
3498       break;
3499   }
3500
3501   return num_special_action;
3502 }
3503
3504
3505 // ============================================================================
3506 // InitGame()
3507 // ----------------------------------------------------------------------------
3508 // initialize and start new game
3509 // ============================================================================
3510
3511 #if DEBUG_INIT_PLAYER
3512 static void DebugPrintPlayerStatus(char *message)
3513 {
3514   int i;
3515
3516   if (!options.debug)
3517     return;
3518
3519   Debug("game:init:player", "%s:", message);
3520
3521   for (i = 0; i < MAX_PLAYERS; i++)
3522   {
3523     struct PlayerInfo *player = &stored_player[i];
3524
3525     Debug("game:init:player",
3526           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3527           i + 1,
3528           player->present,
3529           player->connected,
3530           player->connected_locally,
3531           player->connected_network,
3532           player->active,
3533           (local_player == player ? " (local player)" : ""));
3534   }
3535 }
3536 #endif
3537
3538 void InitGame(void)
3539 {
3540   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3541   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3542   int fade_mask = REDRAW_FIELD;
3543
3544   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3545   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3546   int initial_move_dir = MV_DOWN;
3547   int i, j, x, y;
3548
3549   // required here to update video display before fading (FIX THIS)
3550   DrawMaskedBorder(REDRAW_DOOR_2);
3551
3552   if (!game.restart_level)
3553     CloseDoor(DOOR_CLOSE_1);
3554
3555   SetGameStatus(GAME_MODE_PLAYING);
3556
3557   if (level_editor_test_game)
3558     FadeSkipNextFadeOut();
3559   else
3560     FadeSetEnterScreen();
3561
3562   if (CheckFadeAll())
3563     fade_mask = REDRAW_ALL;
3564
3565   FadeLevelSoundsAndMusic();
3566
3567   ExpireSoundLoops(TRUE);
3568
3569   FadeOut(fade_mask);
3570
3571   if (level_editor_test_game)
3572     FadeSkipNextFadeIn();
3573
3574   // needed if different viewport properties defined for playing
3575   ChangeViewportPropertiesIfNeeded();
3576
3577   ClearField();
3578
3579   DrawCompleteVideoDisplay();
3580
3581   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3582
3583   InitGameEngine();
3584   InitGameControlValues();
3585
3586   if (tape.recording)
3587   {
3588     // initialize tape actions from game when recording tape
3589     tape.use_key_actions   = game.use_key_actions;
3590     tape.use_mouse_actions = game.use_mouse_actions;
3591
3592     // initialize visible playfield size when recording tape (for team mode)
3593     tape.scr_fieldx = SCR_FIELDX;
3594     tape.scr_fieldy = SCR_FIELDY;
3595   }
3596
3597   // don't play tapes over network
3598   network_playing = (network.enabled && !tape.playing);
3599
3600   for (i = 0; i < MAX_PLAYERS; i++)
3601   {
3602     struct PlayerInfo *player = &stored_player[i];
3603
3604     player->index_nr = i;
3605     player->index_bit = (1 << i);
3606     player->element_nr = EL_PLAYER_1 + i;
3607
3608     player->present = FALSE;
3609     player->active = FALSE;
3610     player->mapped = FALSE;
3611
3612     player->killed = FALSE;
3613     player->reanimated = FALSE;
3614     player->buried = FALSE;
3615
3616     player->action = 0;
3617     player->effective_action = 0;
3618     player->programmed_action = 0;
3619     player->snap_action = 0;
3620
3621     player->mouse_action.lx = 0;
3622     player->mouse_action.ly = 0;
3623     player->mouse_action.button = 0;
3624     player->mouse_action.button_hint = 0;
3625
3626     player->effective_mouse_action.lx = 0;
3627     player->effective_mouse_action.ly = 0;
3628     player->effective_mouse_action.button = 0;
3629     player->effective_mouse_action.button_hint = 0;
3630
3631     for (j = 0; j < MAX_NUM_KEYS; j++)
3632       player->key[j] = FALSE;
3633
3634     player->num_white_keys = 0;
3635
3636     player->dynabomb_count = 0;
3637     player->dynabomb_size = 1;
3638     player->dynabombs_left = 0;
3639     player->dynabomb_xl = FALSE;
3640
3641     player->MovDir = initial_move_dir;
3642     player->MovPos = 0;
3643     player->GfxPos = 0;
3644     player->GfxDir = initial_move_dir;
3645     player->GfxAction = ACTION_DEFAULT;
3646     player->Frame = 0;
3647     player->StepFrame = 0;
3648
3649     player->initial_element = player->element_nr;
3650     player->artwork_element =
3651       (level.use_artwork_element[i] ? level.artwork_element[i] :
3652        player->element_nr);
3653     player->use_murphy = FALSE;
3654
3655     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3656     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3657
3658     player->gravity = level.initial_player_gravity[i];
3659
3660     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3661
3662     player->actual_frame_counter = 0;
3663
3664     player->step_counter = 0;
3665
3666     player->last_move_dir = initial_move_dir;
3667
3668     player->is_active = FALSE;
3669
3670     player->is_waiting = FALSE;
3671     player->is_moving = FALSE;
3672     player->is_auto_moving = FALSE;
3673     player->is_digging = FALSE;
3674     player->is_snapping = FALSE;
3675     player->is_collecting = FALSE;
3676     player->is_pushing = FALSE;
3677     player->is_switching = FALSE;
3678     player->is_dropping = FALSE;
3679     player->is_dropping_pressed = FALSE;
3680
3681     player->is_bored = FALSE;
3682     player->is_sleeping = FALSE;
3683
3684     player->was_waiting = TRUE;
3685     player->was_moving = FALSE;
3686     player->was_snapping = FALSE;
3687     player->was_dropping = FALSE;
3688
3689     player->force_dropping = FALSE;
3690
3691     player->frame_counter_bored = -1;
3692     player->frame_counter_sleeping = -1;
3693
3694     player->anim_delay_counter = 0;
3695     player->post_delay_counter = 0;
3696
3697     player->dir_waiting = initial_move_dir;
3698     player->action_waiting = ACTION_DEFAULT;
3699     player->last_action_waiting = ACTION_DEFAULT;
3700     player->special_action_bored = ACTION_DEFAULT;
3701     player->special_action_sleeping = ACTION_DEFAULT;
3702
3703     player->switch_x = -1;
3704     player->switch_y = -1;
3705
3706     player->drop_x = -1;
3707     player->drop_y = -1;
3708
3709     player->show_envelope = 0;
3710
3711     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3712
3713     player->push_delay       = -1;      // initialized when pushing starts
3714     player->push_delay_value = game.initial_push_delay_value;
3715
3716     player->drop_delay = 0;
3717     player->drop_pressed_delay = 0;
3718
3719     player->last_jx = -1;
3720     player->last_jy = -1;
3721     player->jx = -1;
3722     player->jy = -1;
3723
3724     player->shield_normal_time_left = 0;
3725     player->shield_deadly_time_left = 0;
3726
3727     player->last_removed_element = EL_UNDEFINED;
3728
3729     player->inventory_infinite_element = EL_UNDEFINED;
3730     player->inventory_size = 0;
3731
3732     if (level.use_initial_inventory[i])
3733     {
3734       for (j = 0; j < level.initial_inventory_size[i]; j++)
3735       {
3736         int element = level.initial_inventory_content[i][j];
3737         int collect_count = element_info[element].collect_count_initial;
3738         int k;
3739
3740         if (!IS_CUSTOM_ELEMENT(element))
3741           collect_count = 1;
3742
3743         if (collect_count == 0)
3744           player->inventory_infinite_element = element;
3745         else
3746           for (k = 0; k < collect_count; k++)
3747             if (player->inventory_size < MAX_INVENTORY_SIZE)
3748               player->inventory_element[player->inventory_size++] = element;
3749       }
3750     }
3751
3752     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3753     SnapField(player, 0, 0);
3754
3755     map_player_action[i] = i;
3756   }
3757
3758   network_player_action_received = FALSE;
3759
3760   // initial null action
3761   if (network_playing)
3762     SendToServer_MovePlayer(MV_NONE);
3763
3764   FrameCounter = 0;
3765   TimeFrames = 0;
3766   TimePlayed = 0;
3767   TimeLeft = level.time;
3768   TapeTime = 0;
3769
3770   ScreenMovDir = MV_NONE;
3771   ScreenMovPos = 0;
3772   ScreenGfxPos = 0;
3773
3774   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3775
3776   game.robot_wheel_x = -1;
3777   game.robot_wheel_y = -1;
3778
3779   game.exit_x = -1;
3780   game.exit_y = -1;
3781
3782   game.all_players_gone = FALSE;
3783
3784   game.LevelSolved = FALSE;
3785   game.GameOver = FALSE;
3786
3787   game.GamePlayed = !tape.playing;
3788
3789   game.LevelSolved_GameWon = FALSE;
3790   game.LevelSolved_GameEnd = FALSE;
3791   game.LevelSolved_SaveTape = FALSE;
3792   game.LevelSolved_SaveScore = FALSE;
3793
3794   game.LevelSolved_CountingTime = 0;
3795   game.LevelSolved_CountingScore = 0;
3796   game.LevelSolved_CountingHealth = 0;
3797
3798   game.panel.active = TRUE;
3799
3800   game.no_time_limit = (level.time == 0);
3801
3802   game.yamyam_content_nr = 0;
3803   game.robot_wheel_active = FALSE;
3804   game.magic_wall_active = FALSE;
3805   game.magic_wall_time_left = 0;
3806   game.light_time_left = 0;
3807   game.timegate_time_left = 0;
3808   game.switchgate_pos = 0;
3809   game.wind_direction = level.wind_direction_initial;
3810
3811   game.time_final = 0;
3812   game.score_time_final = 0;
3813
3814   game.score = 0;
3815   game.score_final = 0;
3816
3817   game.health = MAX_HEALTH;
3818   game.health_final = MAX_HEALTH;
3819
3820   game.gems_still_needed = level.gems_needed;
3821   game.sokoban_fields_still_needed = 0;
3822   game.sokoban_objects_still_needed = 0;
3823   game.lights_still_needed = 0;
3824   game.players_still_needed = 0;
3825   game.friends_still_needed = 0;
3826
3827   game.lenses_time_left = 0;
3828   game.magnify_time_left = 0;
3829
3830   game.ball_active = level.ball_active_initial;
3831   game.ball_content_nr = 0;
3832
3833   game.explosions_delayed = TRUE;
3834
3835   game.envelope_active = FALSE;
3836
3837   for (i = 0; i < NUM_BELTS; i++)
3838   {
3839     game.belt_dir[i] = MV_NONE;
3840     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3841   }
3842
3843   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3844     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3845
3846 #if DEBUG_INIT_PLAYER
3847   DebugPrintPlayerStatus("Player status at level initialization");
3848 #endif
3849
3850   SCAN_PLAYFIELD(x, y)
3851   {
3852     Tile[x][y] = Last[x][y] = level.field[x][y];
3853     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3854     ChangeDelay[x][y] = 0;
3855     ChangePage[x][y] = -1;
3856     CustomValue[x][y] = 0;              // initialized in InitField()
3857     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3858     AmoebaNr[x][y] = 0;
3859     WasJustMoving[x][y] = 0;
3860     WasJustFalling[x][y] = 0;
3861     CheckCollision[x][y] = 0;
3862     CheckImpact[x][y] = 0;
3863     Stop[x][y] = FALSE;
3864     Pushed[x][y] = FALSE;
3865
3866     ChangeCount[x][y] = 0;
3867     ChangeEvent[x][y] = -1;
3868
3869     ExplodePhase[x][y] = 0;
3870     ExplodeDelay[x][y] = 0;
3871     ExplodeField[x][y] = EX_TYPE_NONE;
3872
3873     RunnerVisit[x][y] = 0;
3874     PlayerVisit[x][y] = 0;
3875
3876     GfxFrame[x][y] = 0;
3877     GfxRandom[x][y] = INIT_GFX_RANDOM();
3878     GfxElement[x][y] = EL_UNDEFINED;
3879     GfxAction[x][y] = ACTION_DEFAULT;
3880     GfxDir[x][y] = MV_NONE;
3881     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3882   }
3883
3884   SCAN_PLAYFIELD(x, y)
3885   {
3886     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3887       emulate_bd = FALSE;
3888     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3889       emulate_sp = FALSE;
3890
3891     InitField(x, y, TRUE);
3892
3893     ResetGfxAnimation(x, y);
3894   }
3895
3896   InitBeltMovement();
3897
3898   for (i = 0; i < MAX_PLAYERS; i++)
3899   {
3900     struct PlayerInfo *player = &stored_player[i];
3901
3902     // set number of special actions for bored and sleeping animation
3903     player->num_special_action_bored =
3904       get_num_special_action(player->artwork_element,
3905                              ACTION_BORING_1, ACTION_BORING_LAST);
3906     player->num_special_action_sleeping =
3907       get_num_special_action(player->artwork_element,
3908                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3909   }
3910
3911   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3912                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3913
3914   // initialize type of slippery elements
3915   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3916   {
3917     if (!IS_CUSTOM_ELEMENT(i))
3918     {
3919       // default: elements slip down either to the left or right randomly
3920       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3921
3922       // SP style elements prefer to slip down on the left side
3923       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3924         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3925
3926       // BD style elements prefer to slip down on the left side
3927       if (game.emulation == EMU_BOULDERDASH)
3928         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3929     }
3930   }
3931
3932   // initialize explosion and ignition delay
3933   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3934   {
3935     if (!IS_CUSTOM_ELEMENT(i))
3936     {
3937       int num_phase = 8;
3938       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3939                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3940                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3941       int last_phase = (num_phase + 1) * delay;
3942       int half_phase = (num_phase / 2) * delay;
3943
3944       element_info[i].explosion_delay = last_phase - 1;
3945       element_info[i].ignition_delay = half_phase;
3946
3947       if (i == EL_BLACK_ORB)
3948         element_info[i].ignition_delay = 1;
3949     }
3950   }
3951
3952   // correct non-moving belts to start moving left
3953   for (i = 0; i < NUM_BELTS; i++)
3954     if (game.belt_dir[i] == MV_NONE)
3955       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3956
3957 #if USE_NEW_PLAYER_ASSIGNMENTS
3958   // use preferred player also in local single-player mode
3959   if (!network.enabled && !game.team_mode)
3960   {
3961     int new_index_nr = setup.network_player_nr;
3962
3963     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3964     {
3965       for (i = 0; i < MAX_PLAYERS; i++)
3966         stored_player[i].connected_locally = FALSE;
3967
3968       stored_player[new_index_nr].connected_locally = TRUE;
3969     }
3970   }
3971
3972   for (i = 0; i < MAX_PLAYERS; i++)
3973   {
3974     stored_player[i].connected = FALSE;
3975
3976     // in network game mode, the local player might not be the first player
3977     if (stored_player[i].connected_locally)
3978       local_player = &stored_player[i];
3979   }
3980
3981   if (!network.enabled)
3982     local_player->connected = TRUE;
3983
3984   if (tape.playing)
3985   {
3986     for (i = 0; i < MAX_PLAYERS; i++)
3987       stored_player[i].connected = tape.player_participates[i];
3988   }
3989   else if (network.enabled)
3990   {
3991     // add team mode players connected over the network (needed for correct
3992     // assignment of player figures from level to locally playing players)
3993
3994     for (i = 0; i < MAX_PLAYERS; i++)
3995       if (stored_player[i].connected_network)
3996         stored_player[i].connected = TRUE;
3997   }
3998   else if (game.team_mode)
3999   {
4000     // try to guess locally connected team mode players (needed for correct
4001     // assignment of player figures from level to locally playing players)
4002
4003     for (i = 0; i < MAX_PLAYERS; i++)
4004       if (setup.input[i].use_joystick ||
4005           setup.input[i].key.left != KSYM_UNDEFINED)
4006         stored_player[i].connected = TRUE;
4007   }
4008
4009 #if DEBUG_INIT_PLAYER
4010   DebugPrintPlayerStatus("Player status after level initialization");
4011 #endif
4012
4013 #if DEBUG_INIT_PLAYER
4014   Debug("game:init:player", "Reassigning players ...");
4015 #endif
4016
4017   // check if any connected player was not found in playfield
4018   for (i = 0; i < MAX_PLAYERS; i++)
4019   {
4020     struct PlayerInfo *player = &stored_player[i];
4021
4022     if (player->connected && !player->present)
4023     {
4024       struct PlayerInfo *field_player = NULL;
4025
4026 #if DEBUG_INIT_PLAYER
4027       Debug("game:init:player",
4028             "- looking for field player for player %d ...", i + 1);
4029 #endif
4030
4031       // assign first free player found that is present in the playfield
4032
4033       // first try: look for unmapped playfield player that is not connected
4034       for (j = 0; j < MAX_PLAYERS; j++)
4035         if (field_player == NULL &&
4036             stored_player[j].present &&
4037             !stored_player[j].mapped &&
4038             !stored_player[j].connected)
4039           field_player = &stored_player[j];
4040
4041       // second try: look for *any* unmapped playfield player
4042       for (j = 0; j < MAX_PLAYERS; j++)
4043         if (field_player == NULL &&
4044             stored_player[j].present &&
4045             !stored_player[j].mapped)
4046           field_player = &stored_player[j];
4047
4048       if (field_player != NULL)
4049       {
4050         int jx = field_player->jx, jy = field_player->jy;
4051
4052 #if DEBUG_INIT_PLAYER
4053         Debug("game:init:player", "- found player %d",
4054               field_player->index_nr + 1);
4055 #endif
4056
4057         player->present = FALSE;
4058         player->active = FALSE;
4059
4060         field_player->present = TRUE;
4061         field_player->active = TRUE;
4062
4063         /*
4064         player->initial_element = field_player->initial_element;
4065         player->artwork_element = field_player->artwork_element;
4066
4067         player->block_last_field       = field_player->block_last_field;
4068         player->block_delay_adjustment = field_player->block_delay_adjustment;
4069         */
4070
4071         StorePlayer[jx][jy] = field_player->element_nr;
4072
4073         field_player->jx = field_player->last_jx = jx;
4074         field_player->jy = field_player->last_jy = jy;
4075
4076         if (local_player == player)
4077           local_player = field_player;
4078
4079         map_player_action[field_player->index_nr] = i;
4080
4081         field_player->mapped = TRUE;
4082
4083 #if DEBUG_INIT_PLAYER
4084         Debug("game:init:player", "- map_player_action[%d] == %d",
4085               field_player->index_nr + 1, i + 1);
4086 #endif
4087       }
4088     }
4089
4090     if (player->connected && player->present)
4091       player->mapped = TRUE;
4092   }
4093
4094 #if DEBUG_INIT_PLAYER
4095   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4096 #endif
4097
4098 #else
4099
4100   // check if any connected player was not found in playfield
4101   for (i = 0; i < MAX_PLAYERS; i++)
4102   {
4103     struct PlayerInfo *player = &stored_player[i];
4104
4105     if (player->connected && !player->present)
4106     {
4107       for (j = 0; j < MAX_PLAYERS; j++)
4108       {
4109         struct PlayerInfo *field_player = &stored_player[j];
4110         int jx = field_player->jx, jy = field_player->jy;
4111
4112         // assign first free player found that is present in the playfield
4113         if (field_player->present && !field_player->connected)
4114         {
4115           player->present = TRUE;
4116           player->active = TRUE;
4117
4118           field_player->present = FALSE;
4119           field_player->active = FALSE;
4120
4121           player->initial_element = field_player->initial_element;
4122           player->artwork_element = field_player->artwork_element;
4123
4124           player->block_last_field       = field_player->block_last_field;
4125           player->block_delay_adjustment = field_player->block_delay_adjustment;
4126
4127           StorePlayer[jx][jy] = player->element_nr;
4128
4129           player->jx = player->last_jx = jx;
4130           player->jy = player->last_jy = jy;
4131
4132           break;
4133         }
4134       }
4135     }
4136   }
4137 #endif
4138
4139 #if 0
4140   Debug("game:init:player", "local_player->present == %d",
4141         local_player->present);
4142 #endif
4143
4144   // set focus to local player for network games, else to all players
4145   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4146   game.centered_player_nr_next = game.centered_player_nr;
4147   game.set_centered_player = FALSE;
4148   game.set_centered_player_wrap = FALSE;
4149
4150   if (network_playing && tape.recording)
4151   {
4152     // store client dependent player focus when recording network games
4153     tape.centered_player_nr_next = game.centered_player_nr_next;
4154     tape.set_centered_player = TRUE;
4155   }
4156
4157   if (tape.playing)
4158   {
4159     // when playing a tape, eliminate all players who do not participate
4160
4161 #if USE_NEW_PLAYER_ASSIGNMENTS
4162
4163     if (!game.team_mode)
4164     {
4165       for (i = 0; i < MAX_PLAYERS; i++)
4166       {
4167         if (stored_player[i].active &&
4168             !tape.player_participates[map_player_action[i]])
4169         {
4170           struct PlayerInfo *player = &stored_player[i];
4171           int jx = player->jx, jy = player->jy;
4172
4173 #if DEBUG_INIT_PLAYER
4174           Debug("game:init:player", "Removing player %d at (%d, %d)",
4175                 i + 1, jx, jy);
4176 #endif
4177
4178           player->active = FALSE;
4179           StorePlayer[jx][jy] = 0;
4180           Tile[jx][jy] = EL_EMPTY;
4181         }
4182       }
4183     }
4184
4185 #else
4186
4187     for (i = 0; i < MAX_PLAYERS; i++)
4188     {
4189       if (stored_player[i].active &&
4190           !tape.player_participates[i])
4191       {
4192         struct PlayerInfo *player = &stored_player[i];
4193         int jx = player->jx, jy = player->jy;
4194
4195         player->active = FALSE;
4196         StorePlayer[jx][jy] = 0;
4197         Tile[jx][jy] = EL_EMPTY;
4198       }
4199     }
4200 #endif
4201   }
4202   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4203   {
4204     // when in single player mode, eliminate all but the local player
4205
4206     for (i = 0; i < MAX_PLAYERS; i++)
4207     {
4208       struct PlayerInfo *player = &stored_player[i];
4209
4210       if (player->active && player != local_player)
4211       {
4212         int jx = player->jx, jy = player->jy;
4213
4214         player->active = FALSE;
4215         player->present = FALSE;
4216
4217         StorePlayer[jx][jy] = 0;
4218         Tile[jx][jy] = EL_EMPTY;
4219       }
4220     }
4221   }
4222
4223   for (i = 0; i < MAX_PLAYERS; i++)
4224     if (stored_player[i].active)
4225       game.players_still_needed++;
4226
4227   if (level.solved_by_one_player)
4228     game.players_still_needed = 1;
4229
4230   // when recording the game, store which players take part in the game
4231   if (tape.recording)
4232   {
4233 #if USE_NEW_PLAYER_ASSIGNMENTS
4234     for (i = 0; i < MAX_PLAYERS; i++)
4235       if (stored_player[i].connected)
4236         tape.player_participates[i] = TRUE;
4237 #else
4238     for (i = 0; i < MAX_PLAYERS; i++)
4239       if (stored_player[i].active)
4240         tape.player_participates[i] = TRUE;
4241 #endif
4242   }
4243
4244 #if DEBUG_INIT_PLAYER
4245   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4246 #endif
4247
4248   if (BorderElement == EL_EMPTY)
4249   {
4250     SBX_Left = 0;
4251     SBX_Right = lev_fieldx - SCR_FIELDX;
4252     SBY_Upper = 0;
4253     SBY_Lower = lev_fieldy - SCR_FIELDY;
4254   }
4255   else
4256   {
4257     SBX_Left = -1;
4258     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4259     SBY_Upper = -1;
4260     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4261   }
4262
4263   if (full_lev_fieldx <= SCR_FIELDX)
4264     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4265   if (full_lev_fieldy <= SCR_FIELDY)
4266     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4267
4268   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4269     SBX_Left--;
4270   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4271     SBY_Upper--;
4272
4273   // if local player not found, look for custom element that might create
4274   // the player (make some assumptions about the right custom element)
4275   if (!local_player->present)
4276   {
4277     int start_x = 0, start_y = 0;
4278     int found_rating = 0;
4279     int found_element = EL_UNDEFINED;
4280     int player_nr = local_player->index_nr;
4281
4282     SCAN_PLAYFIELD(x, y)
4283     {
4284       int element = Tile[x][y];
4285       int content;
4286       int xx, yy;
4287       boolean is_player;
4288
4289       if (level.use_start_element[player_nr] &&
4290           level.start_element[player_nr] == element &&
4291           found_rating < 4)
4292       {
4293         start_x = x;
4294         start_y = y;
4295
4296         found_rating = 4;
4297         found_element = element;
4298       }
4299
4300       if (!IS_CUSTOM_ELEMENT(element))
4301         continue;
4302
4303       if (CAN_CHANGE(element))
4304       {
4305         for (i = 0; i < element_info[element].num_change_pages; i++)
4306         {
4307           // check for player created from custom element as single target
4308           content = element_info[element].change_page[i].target_element;
4309           is_player = IS_PLAYER_ELEMENT(content);
4310
4311           if (is_player && (found_rating < 3 ||
4312                             (found_rating == 3 && element < found_element)))
4313           {
4314             start_x = x;
4315             start_y = y;
4316
4317             found_rating = 3;
4318             found_element = element;
4319           }
4320         }
4321       }
4322
4323       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4324       {
4325         // check for player created from custom element as explosion content
4326         content = element_info[element].content.e[xx][yy];
4327         is_player = IS_PLAYER_ELEMENT(content);
4328
4329         if (is_player && (found_rating < 2 ||
4330                           (found_rating == 2 && element < found_element)))
4331         {
4332           start_x = x + xx - 1;
4333           start_y = y + yy - 1;
4334
4335           found_rating = 2;
4336           found_element = element;
4337         }
4338
4339         if (!CAN_CHANGE(element))
4340           continue;
4341
4342         for (i = 0; i < element_info[element].num_change_pages; i++)
4343         {
4344           // check for player created from custom element as extended target
4345           content =
4346             element_info[element].change_page[i].target_content.e[xx][yy];
4347
4348           is_player = IS_PLAYER_ELEMENT(content);
4349
4350           if (is_player && (found_rating < 1 ||
4351                             (found_rating == 1 && element < found_element)))
4352           {
4353             start_x = x + xx - 1;
4354             start_y = y + yy - 1;
4355
4356             found_rating = 1;
4357             found_element = element;
4358           }
4359         }
4360       }
4361     }
4362
4363     scroll_x = SCROLL_POSITION_X(start_x);
4364     scroll_y = SCROLL_POSITION_Y(start_y);
4365   }
4366   else
4367   {
4368     scroll_x = SCROLL_POSITION_X(local_player->jx);
4369     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4370   }
4371
4372   // !!! FIX THIS (START) !!!
4373   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4374   {
4375     InitGameEngine_EM();
4376   }
4377   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4378   {
4379     InitGameEngine_SP();
4380   }
4381   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4382   {
4383     InitGameEngine_MM();
4384   }
4385   else
4386   {
4387     DrawLevel(REDRAW_FIELD);
4388     DrawAllPlayers();
4389
4390     // after drawing the level, correct some elements
4391     if (game.timegate_time_left == 0)
4392       CloseAllOpenTimegates();
4393   }
4394
4395   // blit playfield from scroll buffer to normal back buffer for fading in
4396   BlitScreenToBitmap(backbuffer);
4397   // !!! FIX THIS (END) !!!
4398
4399   DrawMaskedBorder(fade_mask);
4400
4401   FadeIn(fade_mask);
4402
4403 #if 1
4404   // full screen redraw is required at this point in the following cases:
4405   // - special editor door undrawn when game was started from level editor
4406   // - drawing area (playfield) was changed and has to be removed completely
4407   redraw_mask = REDRAW_ALL;
4408   BackToFront();
4409 #endif
4410
4411   if (!game.restart_level)
4412   {
4413     // copy default game door content to main double buffer
4414
4415     // !!! CHECK AGAIN !!!
4416     SetPanelBackground();
4417     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4418     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4419   }
4420
4421   SetPanelBackground();
4422   SetDrawBackgroundMask(REDRAW_DOOR_1);
4423
4424   UpdateAndDisplayGameControlValues();
4425
4426   if (!game.restart_level)
4427   {
4428     UnmapGameButtons();
4429     UnmapTapeButtons();
4430
4431     FreeGameButtons();
4432     CreateGameButtons();
4433
4434     MapGameButtons();
4435     MapTapeButtons();
4436
4437     // copy actual game door content to door double buffer for OpenDoor()
4438     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4439
4440     OpenDoor(DOOR_OPEN_ALL);
4441
4442     KeyboardAutoRepeatOffUnlessAutoplay();
4443
4444 #if DEBUG_INIT_PLAYER
4445     DebugPrintPlayerStatus("Player status (final)");
4446 #endif
4447   }
4448
4449   UnmapAllGadgets();
4450
4451   MapGameButtons();
4452   MapTapeButtons();
4453
4454   if (!game.restart_level && !tape.playing)
4455   {
4456     LevelStats_incPlayed(level_nr);
4457
4458     SaveLevelSetup_SeriesInfo();
4459   }
4460
4461   game.restart_level = FALSE;
4462   game.restart_game_message = NULL;
4463
4464   game.request_active = FALSE;
4465   game.request_active_or_moving = FALSE;
4466
4467   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4468     InitGameActions_MM();
4469
4470   SaveEngineSnapshotToListInitial();
4471
4472   if (!game.restart_level)
4473   {
4474     PlaySound(SND_GAME_STARTING);
4475
4476     if (setup.sound_music)
4477       PlayLevelMusic();
4478   }
4479 }
4480
4481 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4482                         int actual_player_x, int actual_player_y)
4483 {
4484   // this is used for non-R'n'D game engines to update certain engine values
4485
4486   // needed to determine if sounds are played within the visible screen area
4487   scroll_x = actual_scroll_x;
4488   scroll_y = actual_scroll_y;
4489
4490   // needed to get player position for "follow finger" playing input method
4491   local_player->jx = actual_player_x;
4492   local_player->jy = actual_player_y;
4493 }
4494
4495 void InitMovDir(int x, int y)
4496 {
4497   int i, element = Tile[x][y];
4498   static int xy[4][2] =
4499   {
4500     {  0, +1 },
4501     { +1,  0 },
4502     {  0, -1 },
4503     { -1,  0 }
4504   };
4505   static int direction[3][4] =
4506   {
4507     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4508     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4509     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4510   };
4511
4512   switch (element)
4513   {
4514     case EL_BUG_RIGHT:
4515     case EL_BUG_UP:
4516     case EL_BUG_LEFT:
4517     case EL_BUG_DOWN:
4518       Tile[x][y] = EL_BUG;
4519       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4520       break;
4521
4522     case EL_SPACESHIP_RIGHT:
4523     case EL_SPACESHIP_UP:
4524     case EL_SPACESHIP_LEFT:
4525     case EL_SPACESHIP_DOWN:
4526       Tile[x][y] = EL_SPACESHIP;
4527       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4528       break;
4529
4530     case EL_BD_BUTTERFLY_RIGHT:
4531     case EL_BD_BUTTERFLY_UP:
4532     case EL_BD_BUTTERFLY_LEFT:
4533     case EL_BD_BUTTERFLY_DOWN:
4534       Tile[x][y] = EL_BD_BUTTERFLY;
4535       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4536       break;
4537
4538     case EL_BD_FIREFLY_RIGHT:
4539     case EL_BD_FIREFLY_UP:
4540     case EL_BD_FIREFLY_LEFT:
4541     case EL_BD_FIREFLY_DOWN:
4542       Tile[x][y] = EL_BD_FIREFLY;
4543       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4544       break;
4545
4546     case EL_PACMAN_RIGHT:
4547     case EL_PACMAN_UP:
4548     case EL_PACMAN_LEFT:
4549     case EL_PACMAN_DOWN:
4550       Tile[x][y] = EL_PACMAN;
4551       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4552       break;
4553
4554     case EL_YAMYAM_LEFT:
4555     case EL_YAMYAM_RIGHT:
4556     case EL_YAMYAM_UP:
4557     case EL_YAMYAM_DOWN:
4558       Tile[x][y] = EL_YAMYAM;
4559       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4560       break;
4561
4562     case EL_SP_SNIKSNAK:
4563       MovDir[x][y] = MV_UP;
4564       break;
4565
4566     case EL_SP_ELECTRON:
4567       MovDir[x][y] = MV_LEFT;
4568       break;
4569
4570     case EL_MOLE_LEFT:
4571     case EL_MOLE_RIGHT:
4572     case EL_MOLE_UP:
4573     case EL_MOLE_DOWN:
4574       Tile[x][y] = EL_MOLE;
4575       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4576       break;
4577
4578     case EL_SPRING_LEFT:
4579     case EL_SPRING_RIGHT:
4580       Tile[x][y] = EL_SPRING;
4581       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4582       break;
4583
4584     default:
4585       if (IS_CUSTOM_ELEMENT(element))
4586       {
4587         struct ElementInfo *ei = &element_info[element];
4588         int move_direction_initial = ei->move_direction_initial;
4589         int move_pattern = ei->move_pattern;
4590
4591         if (move_direction_initial == MV_START_PREVIOUS)
4592         {
4593           if (MovDir[x][y] != MV_NONE)
4594             return;
4595
4596           move_direction_initial = MV_START_AUTOMATIC;
4597         }
4598
4599         if (move_direction_initial == MV_START_RANDOM)
4600           MovDir[x][y] = 1 << RND(4);
4601         else if (move_direction_initial & MV_ANY_DIRECTION)
4602           MovDir[x][y] = move_direction_initial;
4603         else if (move_pattern == MV_ALL_DIRECTIONS ||
4604                  move_pattern == MV_TURNING_LEFT ||
4605                  move_pattern == MV_TURNING_RIGHT ||
4606                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4607                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4608                  move_pattern == MV_TURNING_RANDOM)
4609           MovDir[x][y] = 1 << RND(4);
4610         else if (move_pattern == MV_HORIZONTAL)
4611           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4612         else if (move_pattern == MV_VERTICAL)
4613           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4614         else if (move_pattern & MV_ANY_DIRECTION)
4615           MovDir[x][y] = element_info[element].move_pattern;
4616         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4617                  move_pattern == MV_ALONG_RIGHT_SIDE)
4618         {
4619           // use random direction as default start direction
4620           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4621             MovDir[x][y] = 1 << RND(4);
4622
4623           for (i = 0; i < NUM_DIRECTIONS; i++)
4624           {
4625             int x1 = x + xy[i][0];
4626             int y1 = y + xy[i][1];
4627
4628             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4629             {
4630               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4631                 MovDir[x][y] = direction[0][i];
4632               else
4633                 MovDir[x][y] = direction[1][i];
4634
4635               break;
4636             }
4637           }
4638         }                
4639       }
4640       else
4641       {
4642         MovDir[x][y] = 1 << RND(4);
4643
4644         if (element != EL_BUG &&
4645             element != EL_SPACESHIP &&
4646             element != EL_BD_BUTTERFLY &&
4647             element != EL_BD_FIREFLY)
4648           break;
4649
4650         for (i = 0; i < NUM_DIRECTIONS; i++)
4651         {
4652           int x1 = x + xy[i][0];
4653           int y1 = y + xy[i][1];
4654
4655           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4656           {
4657             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4658             {
4659               MovDir[x][y] = direction[0][i];
4660               break;
4661             }
4662             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4663                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4664             {
4665               MovDir[x][y] = direction[1][i];
4666               break;
4667             }
4668           }
4669         }
4670       }
4671       break;
4672   }
4673
4674   GfxDir[x][y] = MovDir[x][y];
4675 }
4676
4677 void InitAmoebaNr(int x, int y)
4678 {
4679   int i;
4680   int group_nr = AmoebaNeighbourNr(x, y);
4681
4682   if (group_nr == 0)
4683   {
4684     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4685     {
4686       if (AmoebaCnt[i] == 0)
4687       {
4688         group_nr = i;
4689         break;
4690       }
4691     }
4692   }
4693
4694   AmoebaNr[x][y] = group_nr;
4695   AmoebaCnt[group_nr]++;
4696   AmoebaCnt2[group_nr]++;
4697 }
4698
4699 static void LevelSolved(void)
4700 {
4701   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4702       game.players_still_needed > 0)
4703     return;
4704
4705   game.LevelSolved = TRUE;
4706   game.GameOver = TRUE;
4707 }
4708
4709 void GameWon(void)
4710 {
4711   static int time_count_steps;
4712   static int time, time_final;
4713   static float score, score_final; // needed for time score < 10 for 10 seconds
4714   static int health, health_final;
4715   static int game_over_delay_1 = 0;
4716   static int game_over_delay_2 = 0;
4717   static int game_over_delay_3 = 0;
4718   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4719   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4720
4721   if (!game.LevelSolved_GameWon)
4722   {
4723     int i;
4724
4725     // do not start end game actions before the player stops moving (to exit)
4726     if (local_player->active && local_player->MovPos)
4727       return;
4728
4729     game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4730     game.score_time_final = (level.use_step_counter ? TimePlayed :
4731                              TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4732
4733     game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4734                         game_em.lev->score :
4735                         level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4736                         game_mm.score :
4737                         game.score);
4738
4739     game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4740                          MM_HEALTH(game_mm.laser_overload_value) :
4741                          game.health);
4742
4743     game.LevelSolved_CountingTime = game.time_final;
4744     game.LevelSolved_CountingScore = game.score_final;
4745     game.LevelSolved_CountingHealth = game.health_final;
4746
4747     game.LevelSolved_GameWon = TRUE;
4748     game.LevelSolved_SaveTape = tape.recording;
4749     game.LevelSolved_SaveScore = !tape.playing;
4750
4751     if (!tape.playing)
4752     {
4753       LevelStats_incSolved(level_nr);
4754
4755       SaveLevelSetup_SeriesInfo();
4756     }
4757
4758     if (tape.auto_play)         // tape might already be stopped here
4759       tape.auto_play_level_solved = TRUE;
4760
4761     TapeStop();
4762
4763     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4764     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4765     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4766
4767     time = time_final = game.time_final;
4768     score = score_final = game.score_final;
4769     health = health_final = game.health_final;
4770
4771     if (time_score > 0)
4772     {
4773       int time_final_max = 999;
4774       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4775       int time_frames = 0;
4776       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4777       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4778
4779       if (TimeLeft > 0)
4780       {
4781         time_final = 0;
4782         time_frames = time_frames_left;
4783       }
4784       else if (game.no_time_limit && TimePlayed < time_final_max)
4785       {
4786         time_final = time_final_max;
4787         time_frames = time_frames_final_max - time_frames_played;
4788       }
4789
4790       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4791
4792       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4793
4794       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4795       {
4796         health_final = 0;
4797         score_final += health * time_score;
4798       }
4799
4800       game.score_final = score_final;
4801       game.health_final = health_final;
4802     }
4803
4804     if (level_editor_test_game || !setup.count_score_after_game)
4805     {
4806       time = time_final;
4807       score = score_final;
4808
4809       game.LevelSolved_CountingTime = time;
4810       game.LevelSolved_CountingScore = score;
4811
4812       game_panel_controls[GAME_PANEL_TIME].value = time;
4813       game_panel_controls[GAME_PANEL_SCORE].value = score;
4814
4815       DisplayGameControlValues();
4816     }
4817
4818     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4819     {
4820       // check if last player has left the level
4821       if (game.exit_x >= 0 &&
4822           game.exit_y >= 0)
4823       {
4824         int x = game.exit_x;
4825         int y = game.exit_y;
4826         int element = Tile[x][y];
4827
4828         // close exit door after last player
4829         if ((game.all_players_gone &&
4830              (element == EL_EXIT_OPEN ||
4831               element == EL_SP_EXIT_OPEN ||
4832               element == EL_STEEL_EXIT_OPEN)) ||
4833             element == EL_EM_EXIT_OPEN ||
4834             element == EL_EM_STEEL_EXIT_OPEN)
4835         {
4836
4837           Tile[x][y] =
4838             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4839              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4840              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4841              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4842              EL_EM_STEEL_EXIT_CLOSING);
4843
4844           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4845         }
4846
4847         // player disappears
4848         DrawLevelField(x, y);
4849       }
4850
4851       for (i = 0; i < MAX_PLAYERS; i++)
4852       {
4853         struct PlayerInfo *player = &stored_player[i];
4854
4855         if (player->present)
4856         {
4857           RemovePlayer(player);
4858
4859           // player disappears
4860           DrawLevelField(player->jx, player->jy);
4861         }
4862       }
4863     }
4864
4865     PlaySound(SND_GAME_WINNING);
4866   }
4867
4868   if (setup.count_score_after_game)
4869   {
4870     if (time != time_final)
4871     {
4872       if (game_over_delay_1 > 0)
4873       {
4874         game_over_delay_1--;
4875
4876         return;
4877       }
4878
4879       int time_to_go = ABS(time_final - time);
4880       int time_count_dir = (time < time_final ? +1 : -1);
4881
4882       if (time_to_go < time_count_steps)
4883         time_count_steps = 1;
4884
4885       time  += time_count_steps * time_count_dir;
4886       score += time_count_steps * time_score;
4887
4888       // set final score to correct rounding differences after counting score
4889       if (time == time_final)
4890         score = score_final;
4891
4892       game.LevelSolved_CountingTime = time;
4893       game.LevelSolved_CountingScore = score;
4894
4895       game_panel_controls[GAME_PANEL_TIME].value = time;
4896       game_panel_controls[GAME_PANEL_SCORE].value = score;
4897
4898       DisplayGameControlValues();
4899
4900       if (time == time_final)
4901         StopSound(SND_GAME_LEVELTIME_BONUS);
4902       else if (setup.sound_loops)
4903         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4904       else
4905         PlaySound(SND_GAME_LEVELTIME_BONUS);
4906
4907       return;
4908     }
4909
4910     if (health != health_final)
4911     {
4912       if (game_over_delay_2 > 0)
4913       {
4914         game_over_delay_2--;
4915
4916         return;
4917       }
4918
4919       int health_count_dir = (health < health_final ? +1 : -1);
4920
4921       health += health_count_dir;
4922       score  += time_score;
4923
4924       game.LevelSolved_CountingHealth = health;
4925       game.LevelSolved_CountingScore = score;
4926
4927       game_panel_controls[GAME_PANEL_HEALTH].value = health;
4928       game_panel_controls[GAME_PANEL_SCORE].value = score;
4929
4930       DisplayGameControlValues();
4931
4932       if (health == health_final)
4933         StopSound(SND_GAME_LEVELTIME_BONUS);
4934       else if (setup.sound_loops)
4935         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4936       else
4937         PlaySound(SND_GAME_LEVELTIME_BONUS);
4938
4939       return;
4940     }
4941   }
4942
4943   game.panel.active = FALSE;
4944
4945   if (game_over_delay_3 > 0)
4946   {
4947     game_over_delay_3--;
4948
4949     return;
4950   }
4951
4952   GameEnd();
4953 }
4954
4955 void GameEnd(void)
4956 {
4957   // used instead of "level_nr" (needed for network games)
4958   int last_level_nr = levelset.level_nr;
4959
4960   game.LevelSolved_GameEnd = TRUE;
4961
4962   if (game.LevelSolved_SaveTape)
4963   {
4964     // make sure that request dialog to save tape does not open door again
4965     if (!global.use_envelope_request)
4966       CloseDoor(DOOR_CLOSE_1);
4967
4968     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4969
4970     // set unique basename for score tape (also saved in high score table)
4971     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
4972   }
4973
4974   // if no tape is to be saved, close both doors simultaneously
4975   CloseDoor(DOOR_CLOSE_ALL);
4976
4977   if (level_editor_test_game)
4978   {
4979     SetGameStatus(GAME_MODE_MAIN);
4980
4981     DrawMainMenu();
4982
4983     return;
4984   }
4985
4986   if (!game.LevelSolved_SaveScore)
4987   {
4988     SetGameStatus(GAME_MODE_MAIN);
4989
4990     DrawMainMenu();
4991
4992     return;
4993   }
4994
4995   if (level_nr == leveldir_current->handicap_level)
4996   {
4997     leveldir_current->handicap_level++;
4998
4999     SaveLevelSetup_SeriesInfo();
5000   }
5001
5002   // save score and score tape before potentially erasing tape below
5003   NewHighScore(last_level_nr);
5004
5005   if (setup.increment_levels &&
5006       level_nr < leveldir_current->last_level &&
5007       !network_playing)
5008   {
5009     level_nr++;         // advance to next level
5010     TapeErase();        // start with empty tape
5011
5012     if (setup.auto_play_next_level)
5013     {
5014       LoadLevel(level_nr);
5015
5016       SaveLevelSetup_SeriesInfo();
5017     }
5018   }
5019
5020   if (scores.last_added >= 0 && setup.show_scores_after_game)
5021   {
5022     SetGameStatus(GAME_MODE_SCORES);
5023
5024     DrawHallOfFame(last_level_nr);
5025   }
5026   else if (setup.auto_play_next_level && setup.increment_levels &&
5027            last_level_nr < leveldir_current->last_level &&
5028            !network_playing)
5029   {
5030     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5031   }
5032   else
5033   {
5034     SetGameStatus(GAME_MODE_MAIN);
5035
5036     DrawMainMenu();
5037   }
5038 }
5039
5040 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5041                          boolean one_score_entry_per_name)
5042 {
5043   int i;
5044
5045   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5046     return -1;
5047
5048   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5049   {
5050     struct ScoreEntry *entry = &list->entry[i];
5051     boolean score_is_better = (new_entry->score >  entry->score);
5052     boolean score_is_equal  = (new_entry->score == entry->score);
5053     boolean time_is_better  = (new_entry->time  <  entry->time);
5054     boolean time_is_equal   = (new_entry->time  == entry->time);
5055     boolean better_by_score = (score_is_better ||
5056                                (score_is_equal && time_is_better));
5057     boolean better_by_time  = (time_is_better ||
5058                                (time_is_equal && score_is_better));
5059     boolean is_better = (level.rate_time_over_score ? better_by_time :
5060                          better_by_score);
5061     boolean entry_is_empty = (entry->score == 0 &&
5062                               entry->time == 0);
5063
5064     // prevent adding server score entries if also existing in local score file
5065     // (special case: historic score entries have an empty tape basename entry)
5066     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5067         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5068       return -1;
5069
5070     if (is_better || entry_is_empty)
5071     {
5072       // player has made it to the hall of fame
5073
5074       if (i < MAX_SCORE_ENTRIES - 1)
5075       {
5076         int m = MAX_SCORE_ENTRIES - 1;
5077         int l;
5078
5079         if (one_score_entry_per_name)
5080         {
5081           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5082             if (strEqual(list->entry[l].name, new_entry->name))
5083               m = l;
5084
5085           if (m == i)   // player's new highscore overwrites his old one
5086             goto put_into_list;
5087         }
5088
5089         for (l = m; l > i; l--)
5090           list->entry[l] = list->entry[l - 1];
5091       }
5092
5093       put_into_list:
5094
5095       *entry = *new_entry;
5096
5097       return i;
5098     }
5099     else if (one_score_entry_per_name &&
5100              strEqual(entry->name, new_entry->name))
5101     {
5102       // player already in high score list with better score or time
5103
5104       return -1;
5105     }
5106   }
5107
5108   return -1;
5109 }
5110
5111 void NewHighScore(int level_nr)
5112 {
5113   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5114   boolean one_per_name = FALSE;
5115
5116   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5117   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5118
5119   new_entry.score = game.score_final;
5120   new_entry.time = game.score_time_final;
5121
5122   LoadScore(level_nr);
5123
5124   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5125
5126   if (scores.last_added >= 0)
5127   {
5128     SaveScore(level_nr);
5129
5130     // store last added local score entry (before merging server scores)
5131     scores.last_added_local = scores.last_added;
5132   }
5133
5134   if (game.LevelSolved_SaveTape)
5135   {
5136     SaveScoreTape(level_nr);
5137     SaveServerScore(level_nr);
5138   }
5139 }
5140
5141 void MergeServerScore(void)
5142 {
5143   struct ScoreEntry last_added_entry;
5144   boolean one_per_name = FALSE;
5145   int i;
5146
5147   if (scores.last_added >= 0)
5148     last_added_entry = scores.entry[scores.last_added];
5149
5150   for (i = 0; i < server_scores.num_entries; i++)
5151   {
5152     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5153
5154     if (pos >= 0 && pos <= scores.last_added)
5155       scores.last_added++;
5156   }
5157
5158   if (scores.last_added >= MAX_SCORE_ENTRIES)
5159   {
5160     scores.last_added = MAX_SCORE_ENTRIES - 1;
5161     scores.force_last_added = TRUE;
5162
5163     scores.entry[scores.last_added] = last_added_entry;
5164   }
5165 }
5166
5167 static int getElementMoveStepsizeExt(int x, int y, int direction)
5168 {
5169   int element = Tile[x][y];
5170   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5171   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5172   int horiz_move = (dx != 0);
5173   int sign = (horiz_move ? dx : dy);
5174   int step = sign * element_info[element].move_stepsize;
5175
5176   // special values for move stepsize for spring and things on conveyor belt
5177   if (horiz_move)
5178   {
5179     if (CAN_FALL(element) &&
5180         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5181       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5182     else if (element == EL_SPRING)
5183       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5184   }
5185
5186   return step;
5187 }
5188
5189 static int getElementMoveStepsize(int x, int y)
5190 {
5191   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5192 }
5193
5194 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5195 {
5196   if (player->GfxAction != action || player->GfxDir != dir)
5197   {
5198     player->GfxAction = action;
5199     player->GfxDir = dir;
5200     player->Frame = 0;
5201     player->StepFrame = 0;
5202   }
5203 }
5204
5205 static void ResetGfxFrame(int x, int y)
5206 {
5207   // profiling showed that "autotest" spends 10~20% of its time in this function
5208   if (DrawingDeactivatedField())
5209     return;
5210
5211   int element = Tile[x][y];
5212   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5213
5214   if (graphic_info[graphic].anim_global_sync)
5215     GfxFrame[x][y] = FrameCounter;
5216   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5217     GfxFrame[x][y] = CustomValue[x][y];
5218   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5219     GfxFrame[x][y] = element_info[element].collect_score;
5220   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5221     GfxFrame[x][y] = ChangeDelay[x][y];
5222 }
5223
5224 static void ResetGfxAnimation(int x, int y)
5225 {
5226   GfxAction[x][y] = ACTION_DEFAULT;
5227   GfxDir[x][y] = MovDir[x][y];
5228   GfxFrame[x][y] = 0;
5229
5230   ResetGfxFrame(x, y);
5231 }
5232
5233 static void ResetRandomAnimationValue(int x, int y)
5234 {
5235   GfxRandom[x][y] = INIT_GFX_RANDOM();
5236 }
5237
5238 static void InitMovingField(int x, int y, int direction)
5239 {
5240   int element = Tile[x][y];
5241   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5242   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5243   int newx = x + dx;
5244   int newy = y + dy;
5245   boolean is_moving_before, is_moving_after;
5246
5247   // check if element was/is moving or being moved before/after mode change
5248   is_moving_before = (WasJustMoving[x][y] != 0);
5249   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5250
5251   // reset animation only for moving elements which change direction of moving
5252   // or which just started or stopped moving
5253   // (else CEs with property "can move" / "not moving" are reset each frame)
5254   if (is_moving_before != is_moving_after ||
5255       direction != MovDir[x][y])
5256     ResetGfxAnimation(x, y);
5257
5258   MovDir[x][y] = direction;
5259   GfxDir[x][y] = direction;
5260
5261   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5262                      direction == MV_DOWN && CAN_FALL(element) ?
5263                      ACTION_FALLING : ACTION_MOVING);
5264
5265   // this is needed for CEs with property "can move" / "not moving"
5266
5267   if (is_moving_after)
5268   {
5269     if (Tile[newx][newy] == EL_EMPTY)
5270       Tile[newx][newy] = EL_BLOCKED;
5271
5272     MovDir[newx][newy] = MovDir[x][y];
5273
5274     CustomValue[newx][newy] = CustomValue[x][y];
5275
5276     GfxFrame[newx][newy] = GfxFrame[x][y];
5277     GfxRandom[newx][newy] = GfxRandom[x][y];
5278     GfxAction[newx][newy] = GfxAction[x][y];
5279     GfxDir[newx][newy] = GfxDir[x][y];
5280   }
5281 }
5282
5283 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5284 {
5285   int direction = MovDir[x][y];
5286   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5287   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5288
5289   *goes_to_x = newx;
5290   *goes_to_y = newy;
5291 }
5292
5293 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5294 {
5295   int oldx = x, oldy = y;
5296   int direction = MovDir[x][y];
5297
5298   if (direction == MV_LEFT)
5299     oldx++;
5300   else if (direction == MV_RIGHT)
5301     oldx--;
5302   else if (direction == MV_UP)
5303     oldy++;
5304   else if (direction == MV_DOWN)
5305     oldy--;
5306
5307   *comes_from_x = oldx;
5308   *comes_from_y = oldy;
5309 }
5310
5311 static int MovingOrBlocked2Element(int x, int y)
5312 {
5313   int element = Tile[x][y];
5314
5315   if (element == EL_BLOCKED)
5316   {
5317     int oldx, oldy;
5318
5319     Blocked2Moving(x, y, &oldx, &oldy);
5320     return Tile[oldx][oldy];
5321   }
5322   else
5323     return element;
5324 }
5325
5326 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5327 {
5328   // like MovingOrBlocked2Element(), but if element is moving
5329   // and (x,y) is the field the moving element is just leaving,
5330   // return EL_BLOCKED instead of the element value
5331   int element = Tile[x][y];
5332
5333   if (IS_MOVING(x, y))
5334   {
5335     if (element == EL_BLOCKED)
5336     {
5337       int oldx, oldy;
5338
5339       Blocked2Moving(x, y, &oldx, &oldy);
5340       return Tile[oldx][oldy];
5341     }
5342     else
5343       return EL_BLOCKED;
5344   }
5345   else
5346     return element;
5347 }
5348
5349 static void RemoveField(int x, int y)
5350 {
5351   Tile[x][y] = EL_EMPTY;
5352
5353   MovPos[x][y] = 0;
5354   MovDir[x][y] = 0;
5355   MovDelay[x][y] = 0;
5356
5357   CustomValue[x][y] = 0;
5358
5359   AmoebaNr[x][y] = 0;
5360   ChangeDelay[x][y] = 0;
5361   ChangePage[x][y] = -1;
5362   Pushed[x][y] = FALSE;
5363
5364   GfxElement[x][y] = EL_UNDEFINED;
5365   GfxAction[x][y] = ACTION_DEFAULT;
5366   GfxDir[x][y] = MV_NONE;
5367 }
5368
5369 static void RemoveMovingField(int x, int y)
5370 {
5371   int oldx = x, oldy = y, newx = x, newy = y;
5372   int element = Tile[x][y];
5373   int next_element = EL_UNDEFINED;
5374
5375   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5376     return;
5377
5378   if (IS_MOVING(x, y))
5379   {
5380     Moving2Blocked(x, y, &newx, &newy);
5381
5382     if (Tile[newx][newy] != EL_BLOCKED)
5383     {
5384       // element is moving, but target field is not free (blocked), but
5385       // already occupied by something different (example: acid pool);
5386       // in this case, only remove the moving field, but not the target
5387
5388       RemoveField(oldx, oldy);
5389
5390       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5391
5392       TEST_DrawLevelField(oldx, oldy);
5393
5394       return;
5395     }
5396   }
5397   else if (element == EL_BLOCKED)
5398   {
5399     Blocked2Moving(x, y, &oldx, &oldy);
5400     if (!IS_MOVING(oldx, oldy))
5401       return;
5402   }
5403
5404   if (element == EL_BLOCKED &&
5405       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5406        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5407        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5408        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5409        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5410        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5411     next_element = get_next_element(Tile[oldx][oldy]);
5412
5413   RemoveField(oldx, oldy);
5414   RemoveField(newx, newy);
5415
5416   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5417
5418   if (next_element != EL_UNDEFINED)
5419     Tile[oldx][oldy] = next_element;
5420
5421   TEST_DrawLevelField(oldx, oldy);
5422   TEST_DrawLevelField(newx, newy);
5423 }
5424
5425 void DrawDynamite(int x, int y)
5426 {
5427   int sx = SCREENX(x), sy = SCREENY(y);
5428   int graphic = el2img(Tile[x][y]);
5429   int frame;
5430
5431   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5432     return;
5433
5434   if (IS_WALKABLE_INSIDE(Back[x][y]))
5435     return;
5436
5437   if (Back[x][y])
5438     DrawLevelElement(x, y, Back[x][y]);
5439   else if (Store[x][y])
5440     DrawLevelElement(x, y, Store[x][y]);
5441   else if (game.use_masked_elements)
5442     DrawLevelElement(x, y, EL_EMPTY);
5443
5444   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5445
5446   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5447     DrawGraphicThruMask(sx, sy, graphic, frame);
5448   else
5449     DrawGraphic(sx, sy, graphic, frame);
5450 }
5451
5452 static void CheckDynamite(int x, int y)
5453 {
5454   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5455   {
5456     MovDelay[x][y]--;
5457
5458     if (MovDelay[x][y] != 0)
5459     {
5460       DrawDynamite(x, y);
5461       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5462
5463       return;
5464     }
5465   }
5466
5467   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5468
5469   Bang(x, y);
5470 }
5471
5472 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5473 {
5474   boolean num_checked_players = 0;
5475   int i;
5476
5477   for (i = 0; i < MAX_PLAYERS; i++)
5478   {
5479     if (stored_player[i].active)
5480     {
5481       int sx = stored_player[i].jx;
5482       int sy = stored_player[i].jy;
5483
5484       if (num_checked_players == 0)
5485       {
5486         *sx1 = *sx2 = sx;
5487         *sy1 = *sy2 = sy;
5488       }
5489       else
5490       {
5491         *sx1 = MIN(*sx1, sx);
5492         *sy1 = MIN(*sy1, sy);
5493         *sx2 = MAX(*sx2, sx);
5494         *sy2 = MAX(*sy2, sy);
5495       }
5496
5497       num_checked_players++;
5498     }
5499   }
5500 }
5501
5502 static boolean checkIfAllPlayersFitToScreen_RND(void)
5503 {
5504   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5505
5506   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5507
5508   return (sx2 - sx1 < SCR_FIELDX &&
5509           sy2 - sy1 < SCR_FIELDY);
5510 }
5511
5512 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5513 {
5514   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5515
5516   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5517
5518   *sx = (sx1 + sx2) / 2;
5519   *sy = (sy1 + sy2) / 2;
5520 }
5521
5522 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5523                                boolean center_screen, boolean quick_relocation)
5524 {
5525   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5526   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5527   boolean no_delay = (tape.warp_forward);
5528   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5529   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5530   int new_scroll_x, new_scroll_y;
5531
5532   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5533   {
5534     // case 1: quick relocation inside visible screen (without scrolling)
5535
5536     RedrawPlayfield();
5537
5538     return;
5539   }
5540
5541   if (!level.shifted_relocation || center_screen)
5542   {
5543     // relocation _with_ centering of screen
5544
5545     new_scroll_x = SCROLL_POSITION_X(x);
5546     new_scroll_y = SCROLL_POSITION_Y(y);
5547   }
5548   else
5549   {
5550     // relocation _without_ centering of screen
5551
5552     int center_scroll_x = SCROLL_POSITION_X(old_x);
5553     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5554     int offset_x = x + (scroll_x - center_scroll_x);
5555     int offset_y = y + (scroll_y - center_scroll_y);
5556
5557     // for new screen position, apply previous offset to center position
5558     new_scroll_x = SCROLL_POSITION_X(offset_x);
5559     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5560   }
5561
5562   if (quick_relocation)
5563   {
5564     // case 2: quick relocation (redraw without visible scrolling)
5565
5566     scroll_x = new_scroll_x;
5567     scroll_y = new_scroll_y;
5568
5569     RedrawPlayfield();
5570
5571     return;
5572   }
5573
5574   // case 3: visible relocation (with scrolling to new position)
5575
5576   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5577
5578   SetVideoFrameDelay(wait_delay_value);
5579
5580   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5581   {
5582     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5583     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5584
5585     if (dx == 0 && dy == 0)             // no scrolling needed at all
5586       break;
5587
5588     scroll_x -= dx;
5589     scroll_y -= dy;
5590
5591     // set values for horizontal/vertical screen scrolling (half tile size)
5592     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5593     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5594     int pos_x = dx * TILEX / 2;
5595     int pos_y = dy * TILEY / 2;
5596     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5597     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5598
5599     ScrollLevel(dx, dy);
5600     DrawAllPlayers();
5601
5602     // scroll in two steps of half tile size to make things smoother
5603     BlitScreenToBitmapExt_RND(window, fx, fy);
5604
5605     // scroll second step to align at full tile size
5606     BlitScreenToBitmap(window);
5607   }
5608
5609   DrawAllPlayers();
5610   BackToFront();
5611
5612   SetVideoFrameDelay(frame_delay_value_old);
5613 }
5614
5615 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5616 {
5617   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5618   int player_nr = GET_PLAYER_NR(el_player);
5619   struct PlayerInfo *player = &stored_player[player_nr];
5620   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5621   boolean no_delay = (tape.warp_forward);
5622   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5623   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5624   int old_jx = player->jx;
5625   int old_jy = player->jy;
5626   int old_element = Tile[old_jx][old_jy];
5627   int element = Tile[jx][jy];
5628   boolean player_relocated = (old_jx != jx || old_jy != jy);
5629
5630   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5631   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5632   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5633   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5634   int leave_side_horiz = move_dir_horiz;
5635   int leave_side_vert  = move_dir_vert;
5636   int enter_side = enter_side_horiz | enter_side_vert;
5637   int leave_side = leave_side_horiz | leave_side_vert;
5638
5639   if (player->buried)           // do not reanimate dead player
5640     return;
5641
5642   if (!player_relocated)        // no need to relocate the player
5643     return;
5644
5645   if (IS_PLAYER(jx, jy))        // player already placed at new position
5646   {
5647     RemoveField(jx, jy);        // temporarily remove newly placed player
5648     DrawLevelField(jx, jy);
5649   }
5650
5651   if (player->present)
5652   {
5653     while (player->MovPos)
5654     {
5655       ScrollPlayer(player, SCROLL_GO_ON);
5656       ScrollScreen(NULL, SCROLL_GO_ON);
5657
5658       AdvanceFrameAndPlayerCounters(player->index_nr);
5659
5660       DrawPlayer(player);
5661
5662       BackToFront_WithFrameDelay(wait_delay_value);
5663     }
5664
5665     DrawPlayer(player);         // needed here only to cleanup last field
5666     DrawLevelField(player->jx, player->jy);     // remove player graphic
5667
5668     player->is_moving = FALSE;
5669   }
5670
5671   if (IS_CUSTOM_ELEMENT(old_element))
5672     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5673                                CE_LEFT_BY_PLAYER,
5674                                player->index_bit, leave_side);
5675
5676   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5677                                       CE_PLAYER_LEAVES_X,
5678                                       player->index_bit, leave_side);
5679
5680   Tile[jx][jy] = el_player;
5681   InitPlayerField(jx, jy, el_player, TRUE);
5682
5683   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5684      possible that the relocation target field did not contain a player element,
5685      but a walkable element, to which the new player was relocated -- in this
5686      case, restore that (already initialized!) element on the player field */
5687   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5688   {
5689     Tile[jx][jy] = element;     // restore previously existing element
5690   }
5691
5692   // only visually relocate centered player
5693   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5694                      FALSE, level.instant_relocation);
5695
5696   TestIfPlayerTouchesBadThing(jx, jy);
5697   TestIfPlayerTouchesCustomElement(jx, jy);
5698
5699   if (IS_CUSTOM_ELEMENT(element))
5700     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5701                                player->index_bit, enter_side);
5702
5703   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5704                                       player->index_bit, enter_side);
5705
5706   if (player->is_switching)
5707   {
5708     /* ensure that relocation while still switching an element does not cause
5709        a new element to be treated as also switched directly after relocation
5710        (this is important for teleporter switches that teleport the player to
5711        a place where another teleporter switch is in the same direction, which
5712        would then incorrectly be treated as immediately switched before the
5713        direction key that caused the switch was released) */
5714
5715     player->switch_x += jx - old_jx;
5716     player->switch_y += jy - old_jy;
5717   }
5718 }
5719
5720 static void Explode(int ex, int ey, int phase, int mode)
5721 {
5722   int x, y;
5723   int last_phase;
5724   int border_element;
5725
5726   // !!! eliminate this variable !!!
5727   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5728
5729   if (game.explosions_delayed)
5730   {
5731     ExplodeField[ex][ey] = mode;
5732     return;
5733   }
5734
5735   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5736   {
5737     int center_element = Tile[ex][ey];
5738     int artwork_element, explosion_element;     // set these values later
5739
5740     // remove things displayed in background while burning dynamite
5741     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5742       Back[ex][ey] = 0;
5743
5744     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5745     {
5746       // put moving element to center field (and let it explode there)
5747       center_element = MovingOrBlocked2Element(ex, ey);
5748       RemoveMovingField(ex, ey);
5749       Tile[ex][ey] = center_element;
5750     }
5751
5752     // now "center_element" is finally determined -- set related values now
5753     artwork_element = center_element;           // for custom player artwork
5754     explosion_element = center_element;         // for custom player artwork
5755
5756     if (IS_PLAYER(ex, ey))
5757     {
5758       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5759
5760       artwork_element = stored_player[player_nr].artwork_element;
5761
5762       if (level.use_explosion_element[player_nr])
5763       {
5764         explosion_element = level.explosion_element[player_nr];
5765         artwork_element = explosion_element;
5766       }
5767     }
5768
5769     if (mode == EX_TYPE_NORMAL ||
5770         mode == EX_TYPE_CENTER ||
5771         mode == EX_TYPE_CROSS)
5772       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5773
5774     last_phase = element_info[explosion_element].explosion_delay + 1;
5775
5776     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5777     {
5778       int xx = x - ex + 1;
5779       int yy = y - ey + 1;
5780       int element;
5781
5782       if (!IN_LEV_FIELD(x, y) ||
5783           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5784           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5785         continue;
5786
5787       element = Tile[x][y];
5788
5789       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5790       {
5791         element = MovingOrBlocked2Element(x, y);
5792
5793         if (!IS_EXPLOSION_PROOF(element))
5794           RemoveMovingField(x, y);
5795       }
5796
5797       // indestructible elements can only explode in center (but not flames)
5798       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5799                                            mode == EX_TYPE_BORDER)) ||
5800           element == EL_FLAMES)
5801         continue;
5802
5803       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5804          behaviour, for example when touching a yamyam that explodes to rocks
5805          with active deadly shield, a rock is created under the player !!! */
5806       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5807 #if 0
5808       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5809           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5810            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5811 #else
5812       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5813 #endif
5814       {
5815         if (IS_ACTIVE_BOMB(element))
5816         {
5817           // re-activate things under the bomb like gate or penguin
5818           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5819           Back[x][y] = 0;
5820         }
5821
5822         continue;
5823       }
5824
5825       // save walkable background elements while explosion on same tile
5826       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5827           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5828         Back[x][y] = element;
5829
5830       // ignite explodable elements reached by other explosion
5831       if (element == EL_EXPLOSION)
5832         element = Store2[x][y];
5833
5834       if (AmoebaNr[x][y] &&
5835           (element == EL_AMOEBA_FULL ||
5836            element == EL_BD_AMOEBA ||
5837            element == EL_AMOEBA_GROWING))
5838       {
5839         AmoebaCnt[AmoebaNr[x][y]]--;
5840         AmoebaCnt2[AmoebaNr[x][y]]--;
5841       }
5842
5843       RemoveField(x, y);
5844
5845       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5846       {
5847         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5848
5849         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5850
5851         if (PLAYERINFO(ex, ey)->use_murphy)
5852           Store[x][y] = EL_EMPTY;
5853       }
5854
5855       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5856       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5857       else if (IS_PLAYER_ELEMENT(center_element))
5858         Store[x][y] = EL_EMPTY;
5859       else if (center_element == EL_YAMYAM)
5860         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5861       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5862         Store[x][y] = element_info[center_element].content.e[xx][yy];
5863 #if 1
5864       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5865       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5866       // otherwise) -- FIX THIS !!!
5867       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5868         Store[x][y] = element_info[element].content.e[1][1];
5869 #else
5870       else if (!CAN_EXPLODE(element))
5871         Store[x][y] = element_info[element].content.e[1][1];
5872 #endif
5873       else
5874         Store[x][y] = EL_EMPTY;
5875
5876       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5877           center_element == EL_AMOEBA_TO_DIAMOND)
5878         Store2[x][y] = element;
5879
5880       Tile[x][y] = EL_EXPLOSION;
5881       GfxElement[x][y] = artwork_element;
5882
5883       ExplodePhase[x][y] = 1;
5884       ExplodeDelay[x][y] = last_phase;
5885
5886       Stop[x][y] = TRUE;
5887     }
5888
5889     if (center_element == EL_YAMYAM)
5890       game.yamyam_content_nr =
5891         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5892
5893     return;
5894   }
5895
5896   if (Stop[ex][ey])
5897     return;
5898
5899   x = ex;
5900   y = ey;
5901
5902   if (phase == 1)
5903     GfxFrame[x][y] = 0;         // restart explosion animation
5904
5905   last_phase = ExplodeDelay[x][y];
5906
5907   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5908
5909   // this can happen if the player leaves an explosion just in time
5910   if (GfxElement[x][y] == EL_UNDEFINED)
5911     GfxElement[x][y] = EL_EMPTY;
5912
5913   border_element = Store2[x][y];
5914   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5915     border_element = StorePlayer[x][y];
5916
5917   if (phase == element_info[border_element].ignition_delay ||
5918       phase == last_phase)
5919   {
5920     boolean border_explosion = FALSE;
5921
5922     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5923         !PLAYER_EXPLOSION_PROTECTED(x, y))
5924     {
5925       KillPlayerUnlessExplosionProtected(x, y);
5926       border_explosion = TRUE;
5927     }
5928     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5929     {
5930       Tile[x][y] = Store2[x][y];
5931       Store2[x][y] = 0;
5932       Bang(x, y);
5933       border_explosion = TRUE;
5934     }
5935     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5936     {
5937       AmoebaToDiamond(x, y);
5938       Store2[x][y] = 0;
5939       border_explosion = TRUE;
5940     }
5941
5942     // if an element just explodes due to another explosion (chain-reaction),
5943     // do not immediately end the new explosion when it was the last frame of
5944     // the explosion (as it would be done in the following "if"-statement!)
5945     if (border_explosion && phase == last_phase)
5946       return;
5947   }
5948
5949   if (phase == last_phase)
5950   {
5951     int element;
5952
5953     element = Tile[x][y] = Store[x][y];
5954     Store[x][y] = Store2[x][y] = 0;
5955     GfxElement[x][y] = EL_UNDEFINED;
5956
5957     // player can escape from explosions and might therefore be still alive
5958     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5959         element <= EL_PLAYER_IS_EXPLODING_4)
5960     {
5961       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5962       int explosion_element = EL_PLAYER_1 + player_nr;
5963       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5964       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5965
5966       if (level.use_explosion_element[player_nr])
5967         explosion_element = level.explosion_element[player_nr];
5968
5969       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5970                     element_info[explosion_element].content.e[xx][yy]);
5971     }
5972
5973     // restore probably existing indestructible background element
5974     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5975       element = Tile[x][y] = Back[x][y];
5976     Back[x][y] = 0;
5977
5978     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5979     GfxDir[x][y] = MV_NONE;
5980     ChangeDelay[x][y] = 0;
5981     ChangePage[x][y] = -1;
5982
5983     CustomValue[x][y] = 0;
5984
5985     InitField_WithBug2(x, y, FALSE);
5986
5987     TEST_DrawLevelField(x, y);
5988
5989     TestIfElementTouchesCustomElement(x, y);
5990
5991     if (GFX_CRUMBLED(element))
5992       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5993
5994     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5995       StorePlayer[x][y] = 0;
5996
5997     if (IS_PLAYER_ELEMENT(element))
5998       RelocatePlayer(x, y, element);
5999   }
6000   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6001   {
6002     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6003     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6004
6005     if (phase == delay)
6006       TEST_DrawLevelFieldCrumbled(x, y);
6007
6008     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6009     {
6010       DrawLevelElement(x, y, Back[x][y]);
6011       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6012     }
6013     else if (IS_WALKABLE_UNDER(Back[x][y]))
6014     {
6015       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6016       DrawLevelElementThruMask(x, y, Back[x][y]);
6017     }
6018     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6019       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6020   }
6021 }
6022
6023 static void DynaExplode(int ex, int ey)
6024 {
6025   int i, j;
6026   int dynabomb_element = Tile[ex][ey];
6027   int dynabomb_size = 1;
6028   boolean dynabomb_xl = FALSE;
6029   struct PlayerInfo *player;
6030   static int xy[4][2] =
6031   {
6032     { 0, -1 },
6033     { -1, 0 },
6034     { +1, 0 },
6035     { 0, +1 }
6036   };
6037
6038   if (IS_ACTIVE_BOMB(dynabomb_element))
6039   {
6040     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6041     dynabomb_size = player->dynabomb_size;
6042     dynabomb_xl = player->dynabomb_xl;
6043     player->dynabombs_left++;
6044   }
6045
6046   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6047
6048   for (i = 0; i < NUM_DIRECTIONS; i++)
6049   {
6050     for (j = 1; j <= dynabomb_size; j++)
6051     {
6052       int x = ex + j * xy[i][0];
6053       int y = ey + j * xy[i][1];
6054       int element;
6055
6056       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6057         break;
6058
6059       element = Tile[x][y];
6060
6061       // do not restart explosions of fields with active bombs
6062       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6063         continue;
6064
6065       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6066
6067       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6068           !IS_DIGGABLE(element) && !dynabomb_xl)
6069         break;
6070     }
6071   }
6072 }
6073
6074 void Bang(int x, int y)
6075 {
6076   int element = MovingOrBlocked2Element(x, y);
6077   int explosion_type = EX_TYPE_NORMAL;
6078
6079   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6080   {
6081     struct PlayerInfo *player = PLAYERINFO(x, y);
6082
6083     element = Tile[x][y] = player->initial_element;
6084
6085     if (level.use_explosion_element[player->index_nr])
6086     {
6087       int explosion_element = level.explosion_element[player->index_nr];
6088
6089       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6090         explosion_type = EX_TYPE_CROSS;
6091       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6092         explosion_type = EX_TYPE_CENTER;
6093     }
6094   }
6095
6096   switch (element)
6097   {
6098     case EL_BUG:
6099     case EL_SPACESHIP:
6100     case EL_BD_BUTTERFLY:
6101     case EL_BD_FIREFLY:
6102     case EL_YAMYAM:
6103     case EL_DARK_YAMYAM:
6104     case EL_ROBOT:
6105     case EL_PACMAN:
6106     case EL_MOLE:
6107       RaiseScoreElement(element);
6108       break;
6109
6110     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6111     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6112     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6113     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6114     case EL_DYNABOMB_INCREASE_NUMBER:
6115     case EL_DYNABOMB_INCREASE_SIZE:
6116     case EL_DYNABOMB_INCREASE_POWER:
6117       explosion_type = EX_TYPE_DYNA;
6118       break;
6119
6120     case EL_DC_LANDMINE:
6121       explosion_type = EX_TYPE_CENTER;
6122       break;
6123
6124     case EL_PENGUIN:
6125     case EL_LAMP:
6126     case EL_LAMP_ACTIVE:
6127     case EL_AMOEBA_TO_DIAMOND:
6128       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6129         explosion_type = EX_TYPE_CENTER;
6130       break;
6131
6132     default:
6133       if (element_info[element].explosion_type == EXPLODES_CROSS)
6134         explosion_type = EX_TYPE_CROSS;
6135       else if (element_info[element].explosion_type == EXPLODES_1X1)
6136         explosion_type = EX_TYPE_CENTER;
6137       break;
6138   }
6139
6140   if (explosion_type == EX_TYPE_DYNA)
6141     DynaExplode(x, y);
6142   else
6143     Explode(x, y, EX_PHASE_START, explosion_type);
6144
6145   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6146 }
6147
6148 static void SplashAcid(int x, int y)
6149 {
6150   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6151       (!IN_LEV_FIELD(x - 1, y - 2) ||
6152        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6153     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6154
6155   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6156       (!IN_LEV_FIELD(x + 1, y - 2) ||
6157        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6158     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6159
6160   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6161 }
6162
6163 static void InitBeltMovement(void)
6164 {
6165   static int belt_base_element[4] =
6166   {
6167     EL_CONVEYOR_BELT_1_LEFT,
6168     EL_CONVEYOR_BELT_2_LEFT,
6169     EL_CONVEYOR_BELT_3_LEFT,
6170     EL_CONVEYOR_BELT_4_LEFT
6171   };
6172   static int belt_base_active_element[4] =
6173   {
6174     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6175     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6176     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6177     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6178   };
6179
6180   int x, y, i, j;
6181
6182   // set frame order for belt animation graphic according to belt direction
6183   for (i = 0; i < NUM_BELTS; i++)
6184   {
6185     int belt_nr = i;
6186
6187     for (j = 0; j < NUM_BELT_PARTS; j++)
6188     {
6189       int element = belt_base_active_element[belt_nr] + j;
6190       int graphic_1 = el2img(element);
6191       int graphic_2 = el2panelimg(element);
6192
6193       if (game.belt_dir[i] == MV_LEFT)
6194       {
6195         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6196         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6197       }
6198       else
6199       {
6200         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6201         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6202       }
6203     }
6204   }
6205
6206   SCAN_PLAYFIELD(x, y)
6207   {
6208     int element = Tile[x][y];
6209
6210     for (i = 0; i < NUM_BELTS; i++)
6211     {
6212       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6213       {
6214         int e_belt_nr = getBeltNrFromBeltElement(element);
6215         int belt_nr = i;
6216
6217         if (e_belt_nr == belt_nr)
6218         {
6219           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6220
6221           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6222         }
6223       }
6224     }
6225   }
6226 }
6227
6228 static void ToggleBeltSwitch(int x, int y)
6229 {
6230   static int belt_base_element[4] =
6231   {
6232     EL_CONVEYOR_BELT_1_LEFT,
6233     EL_CONVEYOR_BELT_2_LEFT,
6234     EL_CONVEYOR_BELT_3_LEFT,
6235     EL_CONVEYOR_BELT_4_LEFT
6236   };
6237   static int belt_base_active_element[4] =
6238   {
6239     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6240     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6241     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6242     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6243   };
6244   static int belt_base_switch_element[4] =
6245   {
6246     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6247     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6248     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6249     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6250   };
6251   static int belt_move_dir[4] =
6252   {
6253     MV_LEFT,
6254     MV_NONE,
6255     MV_RIGHT,
6256     MV_NONE,
6257   };
6258
6259   int element = Tile[x][y];
6260   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6261   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6262   int belt_dir = belt_move_dir[belt_dir_nr];
6263   int xx, yy, i;
6264
6265   if (!IS_BELT_SWITCH(element))
6266     return;
6267
6268   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6269   game.belt_dir[belt_nr] = belt_dir;
6270
6271   if (belt_dir_nr == 3)
6272     belt_dir_nr = 1;
6273
6274   // set frame order for belt animation graphic according to belt direction
6275   for (i = 0; i < NUM_BELT_PARTS; i++)
6276   {
6277     int element = belt_base_active_element[belt_nr] + i;
6278     int graphic_1 = el2img(element);
6279     int graphic_2 = el2panelimg(element);
6280
6281     if (belt_dir == MV_LEFT)
6282     {
6283       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6284       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6285     }
6286     else
6287     {
6288       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6289       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6290     }
6291   }
6292
6293   SCAN_PLAYFIELD(xx, yy)
6294   {
6295     int element = Tile[xx][yy];
6296
6297     if (IS_BELT_SWITCH(element))
6298     {
6299       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6300
6301       if (e_belt_nr == belt_nr)
6302       {
6303         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6304         TEST_DrawLevelField(xx, yy);
6305       }
6306     }
6307     else if (IS_BELT(element) && belt_dir != MV_NONE)
6308     {
6309       int e_belt_nr = getBeltNrFromBeltElement(element);
6310
6311       if (e_belt_nr == belt_nr)
6312       {
6313         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6314
6315         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6316         TEST_DrawLevelField(xx, yy);
6317       }
6318     }
6319     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6320     {
6321       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6322
6323       if (e_belt_nr == belt_nr)
6324       {
6325         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6326
6327         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6328         TEST_DrawLevelField(xx, yy);
6329       }
6330     }
6331   }
6332 }
6333
6334 static void ToggleSwitchgateSwitch(int x, int y)
6335 {
6336   int xx, yy;
6337
6338   game.switchgate_pos = !game.switchgate_pos;
6339
6340   SCAN_PLAYFIELD(xx, yy)
6341   {
6342     int element = Tile[xx][yy];
6343
6344     if (element == EL_SWITCHGATE_SWITCH_UP)
6345     {
6346       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6347       TEST_DrawLevelField(xx, yy);
6348     }
6349     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6350     {
6351       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6352       TEST_DrawLevelField(xx, yy);
6353     }
6354     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6355     {
6356       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6357       TEST_DrawLevelField(xx, yy);
6358     }
6359     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6360     {
6361       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6362       TEST_DrawLevelField(xx, yy);
6363     }
6364     else if (element == EL_SWITCHGATE_OPEN ||
6365              element == EL_SWITCHGATE_OPENING)
6366     {
6367       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6368
6369       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6370     }
6371     else if (element == EL_SWITCHGATE_CLOSED ||
6372              element == EL_SWITCHGATE_CLOSING)
6373     {
6374       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6375
6376       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6377     }
6378   }
6379 }
6380
6381 static int getInvisibleActiveFromInvisibleElement(int element)
6382 {
6383   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6384           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6385           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6386           element);
6387 }
6388
6389 static int getInvisibleFromInvisibleActiveElement(int element)
6390 {
6391   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6392           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6393           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6394           element);
6395 }
6396
6397 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6398 {
6399   int x, y;
6400
6401   SCAN_PLAYFIELD(x, y)
6402   {
6403     int element = Tile[x][y];
6404
6405     if (element == EL_LIGHT_SWITCH &&
6406         game.light_time_left > 0)
6407     {
6408       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6409       TEST_DrawLevelField(x, y);
6410     }
6411     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6412              game.light_time_left == 0)
6413     {
6414       Tile[x][y] = EL_LIGHT_SWITCH;
6415       TEST_DrawLevelField(x, y);
6416     }
6417     else if (element == EL_EMC_DRIPPER &&
6418              game.light_time_left > 0)
6419     {
6420       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6421       TEST_DrawLevelField(x, y);
6422     }
6423     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6424              game.light_time_left == 0)
6425     {
6426       Tile[x][y] = EL_EMC_DRIPPER;
6427       TEST_DrawLevelField(x, y);
6428     }
6429     else if (element == EL_INVISIBLE_STEELWALL ||
6430              element == EL_INVISIBLE_WALL ||
6431              element == EL_INVISIBLE_SAND)
6432     {
6433       if (game.light_time_left > 0)
6434         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6435
6436       TEST_DrawLevelField(x, y);
6437
6438       // uncrumble neighbour fields, if needed
6439       if (element == EL_INVISIBLE_SAND)
6440         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6441     }
6442     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6443              element == EL_INVISIBLE_WALL_ACTIVE ||
6444              element == EL_INVISIBLE_SAND_ACTIVE)
6445     {
6446       if (game.light_time_left == 0)
6447         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6448
6449       TEST_DrawLevelField(x, y);
6450
6451       // re-crumble neighbour fields, if needed
6452       if (element == EL_INVISIBLE_SAND)
6453         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6454     }
6455   }
6456 }
6457
6458 static void RedrawAllInvisibleElementsForLenses(void)
6459 {
6460   int x, y;
6461
6462   SCAN_PLAYFIELD(x, y)
6463   {
6464     int element = Tile[x][y];
6465
6466     if (element == EL_EMC_DRIPPER &&
6467         game.lenses_time_left > 0)
6468     {
6469       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6470       TEST_DrawLevelField(x, y);
6471     }
6472     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6473              game.lenses_time_left == 0)
6474     {
6475       Tile[x][y] = EL_EMC_DRIPPER;
6476       TEST_DrawLevelField(x, y);
6477     }
6478     else if (element == EL_INVISIBLE_STEELWALL ||
6479              element == EL_INVISIBLE_WALL ||
6480              element == EL_INVISIBLE_SAND)
6481     {
6482       if (game.lenses_time_left > 0)
6483         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6484
6485       TEST_DrawLevelField(x, y);
6486
6487       // uncrumble neighbour fields, if needed
6488       if (element == EL_INVISIBLE_SAND)
6489         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6490     }
6491     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6492              element == EL_INVISIBLE_WALL_ACTIVE ||
6493              element == EL_INVISIBLE_SAND_ACTIVE)
6494     {
6495       if (game.lenses_time_left == 0)
6496         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6497
6498       TEST_DrawLevelField(x, y);
6499
6500       // re-crumble neighbour fields, if needed
6501       if (element == EL_INVISIBLE_SAND)
6502         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6503     }
6504   }
6505 }
6506
6507 static void RedrawAllInvisibleElementsForMagnifier(void)
6508 {
6509   int x, y;
6510
6511   SCAN_PLAYFIELD(x, y)
6512   {
6513     int element = Tile[x][y];
6514
6515     if (element == EL_EMC_FAKE_GRASS &&
6516         game.magnify_time_left > 0)
6517     {
6518       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6519       TEST_DrawLevelField(x, y);
6520     }
6521     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6522              game.magnify_time_left == 0)
6523     {
6524       Tile[x][y] = EL_EMC_FAKE_GRASS;
6525       TEST_DrawLevelField(x, y);
6526     }
6527     else if (IS_GATE_GRAY(element) &&
6528              game.magnify_time_left > 0)
6529     {
6530       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6531                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6532                     IS_EM_GATE_GRAY(element) ?
6533                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6534                     IS_EMC_GATE_GRAY(element) ?
6535                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6536                     IS_DC_GATE_GRAY(element) ?
6537                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6538                     element);
6539       TEST_DrawLevelField(x, y);
6540     }
6541     else if (IS_GATE_GRAY_ACTIVE(element) &&
6542              game.magnify_time_left == 0)
6543     {
6544       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6545                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6546                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6547                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6548                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6549                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6550                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6551                     EL_DC_GATE_WHITE_GRAY :
6552                     element);
6553       TEST_DrawLevelField(x, y);
6554     }
6555   }
6556 }
6557
6558 static void ToggleLightSwitch(int x, int y)
6559 {
6560   int element = Tile[x][y];
6561
6562   game.light_time_left =
6563     (element == EL_LIGHT_SWITCH ?
6564      level.time_light * FRAMES_PER_SECOND : 0);
6565
6566   RedrawAllLightSwitchesAndInvisibleElements();
6567 }
6568
6569 static void ActivateTimegateSwitch(int x, int y)
6570 {
6571   int xx, yy;
6572
6573   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6574
6575   SCAN_PLAYFIELD(xx, yy)
6576   {
6577     int element = Tile[xx][yy];
6578
6579     if (element == EL_TIMEGATE_CLOSED ||
6580         element == EL_TIMEGATE_CLOSING)
6581     {
6582       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6583       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6584     }
6585
6586     /*
6587     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6588     {
6589       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6590       TEST_DrawLevelField(xx, yy);
6591     }
6592     */
6593
6594   }
6595
6596   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6597                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6598 }
6599
6600 static void Impact(int x, int y)
6601 {
6602   boolean last_line = (y == lev_fieldy - 1);
6603   boolean object_hit = FALSE;
6604   boolean impact = (last_line || object_hit);
6605   int element = Tile[x][y];
6606   int smashed = EL_STEELWALL;
6607
6608   if (!last_line)       // check if element below was hit
6609   {
6610     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6611       return;
6612
6613     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6614                                          MovDir[x][y + 1] != MV_DOWN ||
6615                                          MovPos[x][y + 1] <= TILEY / 2));
6616
6617     // do not smash moving elements that left the smashed field in time
6618     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6619         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6620       object_hit = FALSE;
6621
6622 #if USE_QUICKSAND_IMPACT_BUGFIX
6623     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6624     {
6625       RemoveMovingField(x, y + 1);
6626       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6627       Tile[x][y + 2] = EL_ROCK;
6628       TEST_DrawLevelField(x, y + 2);
6629
6630       object_hit = TRUE;
6631     }
6632
6633     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6634     {
6635       RemoveMovingField(x, y + 1);
6636       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6637       Tile[x][y + 2] = EL_ROCK;
6638       TEST_DrawLevelField(x, y + 2);
6639
6640       object_hit = TRUE;
6641     }
6642 #endif
6643
6644     if (object_hit)
6645       smashed = MovingOrBlocked2Element(x, y + 1);
6646
6647     impact = (last_line || object_hit);
6648   }
6649
6650   if (!last_line && smashed == EL_ACID) // element falls into acid
6651   {
6652     SplashAcid(x, y + 1);
6653     return;
6654   }
6655
6656   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6657   // only reset graphic animation if graphic really changes after impact
6658   if (impact &&
6659       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6660   {
6661     ResetGfxAnimation(x, y);
6662     TEST_DrawLevelField(x, y);
6663   }
6664
6665   if (impact && CAN_EXPLODE_IMPACT(element))
6666   {
6667     Bang(x, y);
6668     return;
6669   }
6670   else if (impact && element == EL_PEARL &&
6671            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6672   {
6673     ResetGfxAnimation(x, y);
6674
6675     Tile[x][y] = EL_PEARL_BREAKING;
6676     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6677     return;
6678   }
6679   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6680   {
6681     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6682
6683     return;
6684   }
6685
6686   if (impact && element == EL_AMOEBA_DROP)
6687   {
6688     if (object_hit && IS_PLAYER(x, y + 1))
6689       KillPlayerUnlessEnemyProtected(x, y + 1);
6690     else if (object_hit && smashed == EL_PENGUIN)
6691       Bang(x, y + 1);
6692     else
6693     {
6694       Tile[x][y] = EL_AMOEBA_GROWING;
6695       Store[x][y] = EL_AMOEBA_WET;
6696
6697       ResetRandomAnimationValue(x, y);
6698     }
6699     return;
6700   }
6701
6702   if (object_hit)               // check which object was hit
6703   {
6704     if ((CAN_PASS_MAGIC_WALL(element) && 
6705          (smashed == EL_MAGIC_WALL ||
6706           smashed == EL_BD_MAGIC_WALL)) ||
6707         (CAN_PASS_DC_MAGIC_WALL(element) &&
6708          smashed == EL_DC_MAGIC_WALL))
6709     {
6710       int xx, yy;
6711       int activated_magic_wall =
6712         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6713          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6714          EL_DC_MAGIC_WALL_ACTIVE);
6715
6716       // activate magic wall / mill
6717       SCAN_PLAYFIELD(xx, yy)
6718       {
6719         if (Tile[xx][yy] == smashed)
6720           Tile[xx][yy] = activated_magic_wall;
6721       }
6722
6723       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6724       game.magic_wall_active = TRUE;
6725
6726       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6727                             SND_MAGIC_WALL_ACTIVATING :
6728                             smashed == EL_BD_MAGIC_WALL ?
6729                             SND_BD_MAGIC_WALL_ACTIVATING :
6730                             SND_DC_MAGIC_WALL_ACTIVATING));
6731     }
6732
6733     if (IS_PLAYER(x, y + 1))
6734     {
6735       if (CAN_SMASH_PLAYER(element))
6736       {
6737         KillPlayerUnlessEnemyProtected(x, y + 1);
6738         return;
6739       }
6740     }
6741     else if (smashed == EL_PENGUIN)
6742     {
6743       if (CAN_SMASH_PLAYER(element))
6744       {
6745         Bang(x, y + 1);
6746         return;
6747       }
6748     }
6749     else if (element == EL_BD_DIAMOND)
6750     {
6751       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6752       {
6753         Bang(x, y + 1);
6754         return;
6755       }
6756     }
6757     else if (((element == EL_SP_INFOTRON ||
6758                element == EL_SP_ZONK) &&
6759               (smashed == EL_SP_SNIKSNAK ||
6760                smashed == EL_SP_ELECTRON ||
6761                smashed == EL_SP_DISK_ORANGE)) ||
6762              (element == EL_SP_INFOTRON &&
6763               smashed == EL_SP_DISK_YELLOW))
6764     {
6765       Bang(x, y + 1);
6766       return;
6767     }
6768     else if (CAN_SMASH_EVERYTHING(element))
6769     {
6770       if (IS_CLASSIC_ENEMY(smashed) ||
6771           CAN_EXPLODE_SMASHED(smashed))
6772       {
6773         Bang(x, y + 1);
6774         return;
6775       }
6776       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6777       {
6778         if (smashed == EL_LAMP ||
6779             smashed == EL_LAMP_ACTIVE)
6780         {
6781           Bang(x, y + 1);
6782           return;
6783         }
6784         else if (smashed == EL_NUT)
6785         {
6786           Tile[x][y + 1] = EL_NUT_BREAKING;
6787           PlayLevelSound(x, y, SND_NUT_BREAKING);
6788           RaiseScoreElement(EL_NUT);
6789           return;
6790         }
6791         else if (smashed == EL_PEARL)
6792         {
6793           ResetGfxAnimation(x, y);
6794
6795           Tile[x][y + 1] = EL_PEARL_BREAKING;
6796           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6797           return;
6798         }
6799         else if (smashed == EL_DIAMOND)
6800         {
6801           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6802           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6803           return;
6804         }
6805         else if (IS_BELT_SWITCH(smashed))
6806         {
6807           ToggleBeltSwitch(x, y + 1);
6808         }
6809         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6810                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6811                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6812                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6813         {
6814           ToggleSwitchgateSwitch(x, y + 1);
6815         }
6816         else if (smashed == EL_LIGHT_SWITCH ||
6817                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6818         {
6819           ToggleLightSwitch(x, y + 1);
6820         }
6821         else
6822         {
6823           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6824
6825           CheckElementChangeBySide(x, y + 1, smashed, element,
6826                                    CE_SWITCHED, CH_SIDE_TOP);
6827           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6828                                             CH_SIDE_TOP);
6829         }
6830       }
6831       else
6832       {
6833         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6834       }
6835     }
6836   }
6837
6838   // play sound of magic wall / mill
6839   if (!last_line &&
6840       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6841        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6842        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6843   {
6844     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6845       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6846     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6847       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6848     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6849       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6850
6851     return;
6852   }
6853
6854   // play sound of object that hits the ground
6855   if (last_line || object_hit)
6856     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6857 }
6858
6859 static void TurnRoundExt(int x, int y)
6860 {
6861   static struct
6862   {
6863     int dx, dy;
6864   } move_xy[] =
6865   {
6866     {  0,  0 },
6867     { -1,  0 },
6868     { +1,  0 },
6869     {  0,  0 },
6870     {  0, -1 },
6871     {  0,  0 }, { 0, 0 }, { 0, 0 },
6872     {  0, +1 }
6873   };
6874   static struct
6875   {
6876     int left, right, back;
6877   } turn[] =
6878   {
6879     { 0,        0,              0        },
6880     { MV_DOWN,  MV_UP,          MV_RIGHT },
6881     { MV_UP,    MV_DOWN,        MV_LEFT  },
6882     { 0,        0,              0        },
6883     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6884     { 0,        0,              0        },
6885     { 0,        0,              0        },
6886     { 0,        0,              0        },
6887     { MV_RIGHT, MV_LEFT,        MV_UP    }
6888   };
6889
6890   int element = Tile[x][y];
6891   int move_pattern = element_info[element].move_pattern;
6892
6893   int old_move_dir = MovDir[x][y];
6894   int left_dir  = turn[old_move_dir].left;
6895   int right_dir = turn[old_move_dir].right;
6896   int back_dir  = turn[old_move_dir].back;
6897
6898   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6899   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6900   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6901   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6902
6903   int left_x  = x + left_dx,  left_y  = y + left_dy;
6904   int right_x = x + right_dx, right_y = y + right_dy;
6905   int move_x  = x + move_dx,  move_y  = y + move_dy;
6906
6907   int xx, yy;
6908
6909   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6910   {
6911     TestIfBadThingTouchesOtherBadThing(x, y);
6912
6913     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6914       MovDir[x][y] = right_dir;
6915     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6916       MovDir[x][y] = left_dir;
6917
6918     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6919       MovDelay[x][y] = 9;
6920     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6921       MovDelay[x][y] = 1;
6922   }
6923   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6924   {
6925     TestIfBadThingTouchesOtherBadThing(x, y);
6926
6927     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6928       MovDir[x][y] = left_dir;
6929     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6930       MovDir[x][y] = right_dir;
6931
6932     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6933       MovDelay[x][y] = 9;
6934     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6935       MovDelay[x][y] = 1;
6936   }
6937   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6938   {
6939     TestIfBadThingTouchesOtherBadThing(x, y);
6940
6941     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6942       MovDir[x][y] = left_dir;
6943     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6944       MovDir[x][y] = right_dir;
6945
6946     if (MovDir[x][y] != old_move_dir)
6947       MovDelay[x][y] = 9;
6948   }
6949   else if (element == EL_YAMYAM)
6950   {
6951     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6952     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6953
6954     if (can_turn_left && can_turn_right)
6955       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6956     else if (can_turn_left)
6957       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6958     else if (can_turn_right)
6959       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6960     else
6961       MovDir[x][y] = back_dir;
6962
6963     MovDelay[x][y] = 16 + 16 * RND(3);
6964   }
6965   else if (element == EL_DARK_YAMYAM)
6966   {
6967     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6968                                                          left_x, left_y);
6969     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6970                                                          right_x, right_y);
6971
6972     if (can_turn_left && can_turn_right)
6973       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6974     else if (can_turn_left)
6975       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6976     else if (can_turn_right)
6977       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6978     else
6979       MovDir[x][y] = back_dir;
6980
6981     MovDelay[x][y] = 16 + 16 * RND(3);
6982   }
6983   else if (element == EL_PACMAN)
6984   {
6985     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6986     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6987
6988     if (can_turn_left && can_turn_right)
6989       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6990     else if (can_turn_left)
6991       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6992     else if (can_turn_right)
6993       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6994     else
6995       MovDir[x][y] = back_dir;
6996
6997     MovDelay[x][y] = 6 + RND(40);
6998   }
6999   else if (element == EL_PIG)
7000   {
7001     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7002     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7003     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7004     boolean should_turn_left, should_turn_right, should_move_on;
7005     int rnd_value = 24;
7006     int rnd = RND(rnd_value);
7007
7008     should_turn_left = (can_turn_left &&
7009                         (!can_move_on ||
7010                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7011                                                    y + back_dy + left_dy)));
7012     should_turn_right = (can_turn_right &&
7013                          (!can_move_on ||
7014                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7015                                                     y + back_dy + right_dy)));
7016     should_move_on = (can_move_on &&
7017                       (!can_turn_left ||
7018                        !can_turn_right ||
7019                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7020                                                  y + move_dy + left_dy) ||
7021                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7022                                                  y + move_dy + right_dy)));
7023
7024     if (should_turn_left || should_turn_right || should_move_on)
7025     {
7026       if (should_turn_left && should_turn_right && should_move_on)
7027         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7028                         rnd < 2 * rnd_value / 3 ? right_dir :
7029                         old_move_dir);
7030       else if (should_turn_left && should_turn_right)
7031         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7032       else if (should_turn_left && should_move_on)
7033         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7034       else if (should_turn_right && should_move_on)
7035         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7036       else if (should_turn_left)
7037         MovDir[x][y] = left_dir;
7038       else if (should_turn_right)
7039         MovDir[x][y] = right_dir;
7040       else if (should_move_on)
7041         MovDir[x][y] = old_move_dir;
7042     }
7043     else if (can_move_on && rnd > rnd_value / 8)
7044       MovDir[x][y] = old_move_dir;
7045     else if (can_turn_left && can_turn_right)
7046       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7047     else if (can_turn_left && rnd > rnd_value / 8)
7048       MovDir[x][y] = left_dir;
7049     else if (can_turn_right && rnd > rnd_value/8)
7050       MovDir[x][y] = right_dir;
7051     else
7052       MovDir[x][y] = back_dir;
7053
7054     xx = x + move_xy[MovDir[x][y]].dx;
7055     yy = y + move_xy[MovDir[x][y]].dy;
7056
7057     if (!IN_LEV_FIELD(xx, yy) ||
7058         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7059       MovDir[x][y] = old_move_dir;
7060
7061     MovDelay[x][y] = 0;
7062   }
7063   else if (element == EL_DRAGON)
7064   {
7065     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7066     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7067     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7068     int rnd_value = 24;
7069     int rnd = RND(rnd_value);
7070
7071     if (can_move_on && rnd > rnd_value / 8)
7072       MovDir[x][y] = old_move_dir;
7073     else if (can_turn_left && can_turn_right)
7074       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7075     else if (can_turn_left && rnd > rnd_value / 8)
7076       MovDir[x][y] = left_dir;
7077     else if (can_turn_right && rnd > rnd_value / 8)
7078       MovDir[x][y] = right_dir;
7079     else
7080       MovDir[x][y] = back_dir;
7081
7082     xx = x + move_xy[MovDir[x][y]].dx;
7083     yy = y + move_xy[MovDir[x][y]].dy;
7084
7085     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7086       MovDir[x][y] = old_move_dir;
7087
7088     MovDelay[x][y] = 0;
7089   }
7090   else if (element == EL_MOLE)
7091   {
7092     boolean can_move_on =
7093       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7094                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7095                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7096     if (!can_move_on)
7097     {
7098       boolean can_turn_left =
7099         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7100                               IS_AMOEBOID(Tile[left_x][left_y])));
7101
7102       boolean can_turn_right =
7103         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7104                               IS_AMOEBOID(Tile[right_x][right_y])));
7105
7106       if (can_turn_left && can_turn_right)
7107         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7108       else if (can_turn_left)
7109         MovDir[x][y] = left_dir;
7110       else
7111         MovDir[x][y] = right_dir;
7112     }
7113
7114     if (MovDir[x][y] != old_move_dir)
7115       MovDelay[x][y] = 9;
7116   }
7117   else if (element == EL_BALLOON)
7118   {
7119     MovDir[x][y] = game.wind_direction;
7120     MovDelay[x][y] = 0;
7121   }
7122   else if (element == EL_SPRING)
7123   {
7124     if (MovDir[x][y] & MV_HORIZONTAL)
7125     {
7126       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7127           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7128       {
7129         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7130         ResetGfxAnimation(move_x, move_y);
7131         TEST_DrawLevelField(move_x, move_y);
7132
7133         MovDir[x][y] = back_dir;
7134       }
7135       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7136                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7137         MovDir[x][y] = MV_NONE;
7138     }
7139
7140     MovDelay[x][y] = 0;
7141   }
7142   else if (element == EL_ROBOT ||
7143            element == EL_SATELLITE ||
7144            element == EL_PENGUIN ||
7145            element == EL_EMC_ANDROID)
7146   {
7147     int attr_x = -1, attr_y = -1;
7148
7149     if (game.all_players_gone)
7150     {
7151       attr_x = game.exit_x;
7152       attr_y = game.exit_y;
7153     }
7154     else
7155     {
7156       int i;
7157
7158       for (i = 0; i < MAX_PLAYERS; i++)
7159       {
7160         struct PlayerInfo *player = &stored_player[i];
7161         int jx = player->jx, jy = player->jy;
7162
7163         if (!player->active)
7164           continue;
7165
7166         if (attr_x == -1 ||
7167             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7168         {
7169           attr_x = jx;
7170           attr_y = jy;
7171         }
7172       }
7173     }
7174
7175     if (element == EL_ROBOT &&
7176         game.robot_wheel_x >= 0 &&
7177         game.robot_wheel_y >= 0 &&
7178         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7179          game.engine_version < VERSION_IDENT(3,1,0,0)))
7180     {
7181       attr_x = game.robot_wheel_x;
7182       attr_y = game.robot_wheel_y;
7183     }
7184
7185     if (element == EL_PENGUIN)
7186     {
7187       int i;
7188       static int xy[4][2] =
7189       {
7190         { 0, -1 },
7191         { -1, 0 },
7192         { +1, 0 },
7193         { 0, +1 }
7194       };
7195
7196       for (i = 0; i < NUM_DIRECTIONS; i++)
7197       {
7198         int ex = x + xy[i][0];
7199         int ey = y + xy[i][1];
7200
7201         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7202                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7203                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7204                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7205         {
7206           attr_x = ex;
7207           attr_y = ey;
7208           break;
7209         }
7210       }
7211     }
7212
7213     MovDir[x][y] = MV_NONE;
7214     if (attr_x < x)
7215       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7216     else if (attr_x > x)
7217       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7218     if (attr_y < y)
7219       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7220     else if (attr_y > y)
7221       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7222
7223     if (element == EL_ROBOT)
7224     {
7225       int newx, newy;
7226
7227       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7228         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7229       Moving2Blocked(x, y, &newx, &newy);
7230
7231       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7232         MovDelay[x][y] = 8 + 8 * !RND(3);
7233       else
7234         MovDelay[x][y] = 16;
7235     }
7236     else if (element == EL_PENGUIN)
7237     {
7238       int newx, newy;
7239
7240       MovDelay[x][y] = 1;
7241
7242       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7243       {
7244         boolean first_horiz = RND(2);
7245         int new_move_dir = MovDir[x][y];
7246
7247         MovDir[x][y] =
7248           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7249         Moving2Blocked(x, y, &newx, &newy);
7250
7251         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7252           return;
7253
7254         MovDir[x][y] =
7255           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7256         Moving2Blocked(x, y, &newx, &newy);
7257
7258         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7259           return;
7260
7261         MovDir[x][y] = old_move_dir;
7262         return;
7263       }
7264     }
7265     else if (element == EL_SATELLITE)
7266     {
7267       int newx, newy;
7268
7269       MovDelay[x][y] = 1;
7270
7271       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7272       {
7273         boolean first_horiz = RND(2);
7274         int new_move_dir = MovDir[x][y];
7275
7276         MovDir[x][y] =
7277           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7278         Moving2Blocked(x, y, &newx, &newy);
7279
7280         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7281           return;
7282
7283         MovDir[x][y] =
7284           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7285         Moving2Blocked(x, y, &newx, &newy);
7286
7287         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7288           return;
7289
7290         MovDir[x][y] = old_move_dir;
7291         return;
7292       }
7293     }
7294     else if (element == EL_EMC_ANDROID)
7295     {
7296       static int check_pos[16] =
7297       {
7298         -1,             //  0 => (invalid)
7299         7,              //  1 => MV_LEFT
7300         3,              //  2 => MV_RIGHT
7301         -1,             //  3 => (invalid)
7302         1,              //  4 =>            MV_UP
7303         0,              //  5 => MV_LEFT  | MV_UP
7304         2,              //  6 => MV_RIGHT | MV_UP
7305         -1,             //  7 => (invalid)
7306         5,              //  8 =>            MV_DOWN
7307         6,              //  9 => MV_LEFT  | MV_DOWN
7308         4,              // 10 => MV_RIGHT | MV_DOWN
7309         -1,             // 11 => (invalid)
7310         -1,             // 12 => (invalid)
7311         -1,             // 13 => (invalid)
7312         -1,             // 14 => (invalid)
7313         -1,             // 15 => (invalid)
7314       };
7315       static struct
7316       {
7317         int dx, dy;
7318         int dir;
7319       } check_xy[8] =
7320       {
7321         { -1, -1,       MV_LEFT  | MV_UP   },
7322         {  0, -1,                  MV_UP   },
7323         { +1, -1,       MV_RIGHT | MV_UP   },
7324         { +1,  0,       MV_RIGHT           },
7325         { +1, +1,       MV_RIGHT | MV_DOWN },
7326         {  0, +1,                  MV_DOWN },
7327         { -1, +1,       MV_LEFT  | MV_DOWN },
7328         { -1,  0,       MV_LEFT            },
7329       };
7330       int start_pos, check_order;
7331       boolean can_clone = FALSE;
7332       int i;
7333
7334       // check if there is any free field around current position
7335       for (i = 0; i < 8; i++)
7336       {
7337         int newx = x + check_xy[i].dx;
7338         int newy = y + check_xy[i].dy;
7339
7340         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7341         {
7342           can_clone = TRUE;
7343
7344           break;
7345         }
7346       }
7347
7348       if (can_clone)            // randomly find an element to clone
7349       {
7350         can_clone = FALSE;
7351
7352         start_pos = check_pos[RND(8)];
7353         check_order = (RND(2) ? -1 : +1);
7354
7355         for (i = 0; i < 8; i++)
7356         {
7357           int pos_raw = start_pos + i * check_order;
7358           int pos = (pos_raw + 8) % 8;
7359           int newx = x + check_xy[pos].dx;
7360           int newy = y + check_xy[pos].dy;
7361
7362           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7363           {
7364             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7365             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7366
7367             Store[x][y] = Tile[newx][newy];
7368
7369             can_clone = TRUE;
7370
7371             break;
7372           }
7373         }
7374       }
7375
7376       if (can_clone)            // randomly find a direction to move
7377       {
7378         can_clone = FALSE;
7379
7380         start_pos = check_pos[RND(8)];
7381         check_order = (RND(2) ? -1 : +1);
7382
7383         for (i = 0; i < 8; i++)
7384         {
7385           int pos_raw = start_pos + i * check_order;
7386           int pos = (pos_raw + 8) % 8;
7387           int newx = x + check_xy[pos].dx;
7388           int newy = y + check_xy[pos].dy;
7389           int new_move_dir = check_xy[pos].dir;
7390
7391           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7392           {
7393             MovDir[x][y] = new_move_dir;
7394             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7395
7396             can_clone = TRUE;
7397
7398             break;
7399           }
7400         }
7401       }
7402
7403       if (can_clone)            // cloning and moving successful
7404         return;
7405
7406       // cannot clone -- try to move towards player
7407
7408       start_pos = check_pos[MovDir[x][y] & 0x0f];
7409       check_order = (RND(2) ? -1 : +1);
7410
7411       for (i = 0; i < 3; i++)
7412       {
7413         // first check start_pos, then previous/next or (next/previous) pos
7414         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7415         int pos = (pos_raw + 8) % 8;
7416         int newx = x + check_xy[pos].dx;
7417         int newy = y + check_xy[pos].dy;
7418         int new_move_dir = check_xy[pos].dir;
7419
7420         if (IS_PLAYER(newx, newy))
7421           break;
7422
7423         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7424         {
7425           MovDir[x][y] = new_move_dir;
7426           MovDelay[x][y] = level.android_move_time * 8 + 1;
7427
7428           break;
7429         }
7430       }
7431     }
7432   }
7433   else if (move_pattern == MV_TURNING_LEFT ||
7434            move_pattern == MV_TURNING_RIGHT ||
7435            move_pattern == MV_TURNING_LEFT_RIGHT ||
7436            move_pattern == MV_TURNING_RIGHT_LEFT ||
7437            move_pattern == MV_TURNING_RANDOM ||
7438            move_pattern == MV_ALL_DIRECTIONS)
7439   {
7440     boolean can_turn_left =
7441       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7442     boolean can_turn_right =
7443       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7444
7445     if (element_info[element].move_stepsize == 0)       // "not moving"
7446       return;
7447
7448     if (move_pattern == MV_TURNING_LEFT)
7449       MovDir[x][y] = left_dir;
7450     else if (move_pattern == MV_TURNING_RIGHT)
7451       MovDir[x][y] = right_dir;
7452     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7453       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7454     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7455       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7456     else if (move_pattern == MV_TURNING_RANDOM)
7457       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7458                       can_turn_right && !can_turn_left ? right_dir :
7459                       RND(2) ? left_dir : right_dir);
7460     else if (can_turn_left && can_turn_right)
7461       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7462     else if (can_turn_left)
7463       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7464     else if (can_turn_right)
7465       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7466     else
7467       MovDir[x][y] = back_dir;
7468
7469     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7470   }
7471   else if (move_pattern == MV_HORIZONTAL ||
7472            move_pattern == MV_VERTICAL)
7473   {
7474     if (move_pattern & old_move_dir)
7475       MovDir[x][y] = back_dir;
7476     else if (move_pattern == MV_HORIZONTAL)
7477       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7478     else if (move_pattern == MV_VERTICAL)
7479       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7480
7481     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7482   }
7483   else if (move_pattern & MV_ANY_DIRECTION)
7484   {
7485     MovDir[x][y] = move_pattern;
7486     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7487   }
7488   else if (move_pattern & MV_WIND_DIRECTION)
7489   {
7490     MovDir[x][y] = game.wind_direction;
7491     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7492   }
7493   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7494   {
7495     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7496       MovDir[x][y] = left_dir;
7497     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7498       MovDir[x][y] = right_dir;
7499
7500     if (MovDir[x][y] != old_move_dir)
7501       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7502   }
7503   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7504   {
7505     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7506       MovDir[x][y] = right_dir;
7507     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7508       MovDir[x][y] = left_dir;
7509
7510     if (MovDir[x][y] != old_move_dir)
7511       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7512   }
7513   else if (move_pattern == MV_TOWARDS_PLAYER ||
7514            move_pattern == MV_AWAY_FROM_PLAYER)
7515   {
7516     int attr_x = -1, attr_y = -1;
7517     int newx, newy;
7518     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7519
7520     if (game.all_players_gone)
7521     {
7522       attr_x = game.exit_x;
7523       attr_y = game.exit_y;
7524     }
7525     else
7526     {
7527       int i;
7528
7529       for (i = 0; i < MAX_PLAYERS; i++)
7530       {
7531         struct PlayerInfo *player = &stored_player[i];
7532         int jx = player->jx, jy = player->jy;
7533
7534         if (!player->active)
7535           continue;
7536
7537         if (attr_x == -1 ||
7538             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7539         {
7540           attr_x = jx;
7541           attr_y = jy;
7542         }
7543       }
7544     }
7545
7546     MovDir[x][y] = MV_NONE;
7547     if (attr_x < x)
7548       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7549     else if (attr_x > x)
7550       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7551     if (attr_y < y)
7552       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7553     else if (attr_y > y)
7554       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7555
7556     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7557
7558     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7559     {
7560       boolean first_horiz = RND(2);
7561       int new_move_dir = MovDir[x][y];
7562
7563       if (element_info[element].move_stepsize == 0)     // "not moving"
7564       {
7565         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7566         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7567
7568         return;
7569       }
7570
7571       MovDir[x][y] =
7572         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7573       Moving2Blocked(x, y, &newx, &newy);
7574
7575       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7576         return;
7577
7578       MovDir[x][y] =
7579         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7580       Moving2Blocked(x, y, &newx, &newy);
7581
7582       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7583         return;
7584
7585       MovDir[x][y] = old_move_dir;
7586     }
7587   }
7588   else if (move_pattern == MV_WHEN_PUSHED ||
7589            move_pattern == MV_WHEN_DROPPED)
7590   {
7591     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7592       MovDir[x][y] = MV_NONE;
7593
7594     MovDelay[x][y] = 0;
7595   }
7596   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7597   {
7598     static int test_xy[7][2] =
7599     {
7600       { 0, -1 },
7601       { -1, 0 },
7602       { +1, 0 },
7603       { 0, +1 },
7604       { 0, -1 },
7605       { -1, 0 },
7606       { +1, 0 },
7607     };
7608     static int test_dir[7] =
7609     {
7610       MV_UP,
7611       MV_LEFT,
7612       MV_RIGHT,
7613       MV_DOWN,
7614       MV_UP,
7615       MV_LEFT,
7616       MV_RIGHT,
7617     };
7618     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7619     int move_preference = -1000000;     // start with very low preference
7620     int new_move_dir = MV_NONE;
7621     int start_test = RND(4);
7622     int i;
7623
7624     for (i = 0; i < NUM_DIRECTIONS; i++)
7625     {
7626       int move_dir = test_dir[start_test + i];
7627       int move_dir_preference;
7628
7629       xx = x + test_xy[start_test + i][0];
7630       yy = y + test_xy[start_test + i][1];
7631
7632       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7633           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7634       {
7635         new_move_dir = move_dir;
7636
7637         break;
7638       }
7639
7640       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7641         continue;
7642
7643       move_dir_preference = -1 * RunnerVisit[xx][yy];
7644       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7645         move_dir_preference = PlayerVisit[xx][yy];
7646
7647       if (move_dir_preference > move_preference)
7648       {
7649         // prefer field that has not been visited for the longest time
7650         move_preference = move_dir_preference;
7651         new_move_dir = move_dir;
7652       }
7653       else if (move_dir_preference == move_preference &&
7654                move_dir == old_move_dir)
7655       {
7656         // prefer last direction when all directions are preferred equally
7657         move_preference = move_dir_preference;
7658         new_move_dir = move_dir;
7659       }
7660     }
7661
7662     MovDir[x][y] = new_move_dir;
7663     if (old_move_dir != new_move_dir)
7664       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7665   }
7666 }
7667
7668 static void TurnRound(int x, int y)
7669 {
7670   int direction = MovDir[x][y];
7671
7672   TurnRoundExt(x, y);
7673
7674   GfxDir[x][y] = MovDir[x][y];
7675
7676   if (direction != MovDir[x][y])
7677     GfxFrame[x][y] = 0;
7678
7679   if (MovDelay[x][y])
7680     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7681
7682   ResetGfxFrame(x, y);
7683 }
7684
7685 static boolean JustBeingPushed(int x, int y)
7686 {
7687   int i;
7688
7689   for (i = 0; i < MAX_PLAYERS; i++)
7690   {
7691     struct PlayerInfo *player = &stored_player[i];
7692
7693     if (player->active && player->is_pushing && player->MovPos)
7694     {
7695       int next_jx = player->jx + (player->jx - player->last_jx);
7696       int next_jy = player->jy + (player->jy - player->last_jy);
7697
7698       if (x == next_jx && y == next_jy)
7699         return TRUE;
7700     }
7701   }
7702
7703   return FALSE;
7704 }
7705
7706 static void StartMoving(int x, int y)
7707 {
7708   boolean started_moving = FALSE;       // some elements can fall _and_ move
7709   int element = Tile[x][y];
7710
7711   if (Stop[x][y])
7712     return;
7713
7714   if (MovDelay[x][y] == 0)
7715     GfxAction[x][y] = ACTION_DEFAULT;
7716
7717   if (CAN_FALL(element) && y < lev_fieldy - 1)
7718   {
7719     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7720         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7721       if (JustBeingPushed(x, y))
7722         return;
7723
7724     if (element == EL_QUICKSAND_FULL)
7725     {
7726       if (IS_FREE(x, y + 1))
7727       {
7728         InitMovingField(x, y, MV_DOWN);
7729         started_moving = TRUE;
7730
7731         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7732 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7733         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7734           Store[x][y] = EL_ROCK;
7735 #else
7736         Store[x][y] = EL_ROCK;
7737 #endif
7738
7739         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7740       }
7741       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7742       {
7743         if (!MovDelay[x][y])
7744         {
7745           MovDelay[x][y] = TILEY + 1;
7746
7747           ResetGfxAnimation(x, y);
7748           ResetGfxAnimation(x, y + 1);
7749         }
7750
7751         if (MovDelay[x][y])
7752         {
7753           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7754           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7755
7756           MovDelay[x][y]--;
7757           if (MovDelay[x][y])
7758             return;
7759         }
7760
7761         Tile[x][y] = EL_QUICKSAND_EMPTY;
7762         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7763         Store[x][y + 1] = Store[x][y];
7764         Store[x][y] = 0;
7765
7766         PlayLevelSoundAction(x, y, ACTION_FILLING);
7767       }
7768       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7769       {
7770         if (!MovDelay[x][y])
7771         {
7772           MovDelay[x][y] = TILEY + 1;
7773
7774           ResetGfxAnimation(x, y);
7775           ResetGfxAnimation(x, y + 1);
7776         }
7777
7778         if (MovDelay[x][y])
7779         {
7780           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7781           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7782
7783           MovDelay[x][y]--;
7784           if (MovDelay[x][y])
7785             return;
7786         }
7787
7788         Tile[x][y] = EL_QUICKSAND_EMPTY;
7789         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7790         Store[x][y + 1] = Store[x][y];
7791         Store[x][y] = 0;
7792
7793         PlayLevelSoundAction(x, y, ACTION_FILLING);
7794       }
7795     }
7796     else if (element == EL_QUICKSAND_FAST_FULL)
7797     {
7798       if (IS_FREE(x, y + 1))
7799       {
7800         InitMovingField(x, y, MV_DOWN);
7801         started_moving = TRUE;
7802
7803         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7804 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7805         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7806           Store[x][y] = EL_ROCK;
7807 #else
7808         Store[x][y] = EL_ROCK;
7809 #endif
7810
7811         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7812       }
7813       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7814       {
7815         if (!MovDelay[x][y])
7816         {
7817           MovDelay[x][y] = TILEY + 1;
7818
7819           ResetGfxAnimation(x, y);
7820           ResetGfxAnimation(x, y + 1);
7821         }
7822
7823         if (MovDelay[x][y])
7824         {
7825           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7826           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7827
7828           MovDelay[x][y]--;
7829           if (MovDelay[x][y])
7830             return;
7831         }
7832
7833         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7834         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7835         Store[x][y + 1] = Store[x][y];
7836         Store[x][y] = 0;
7837
7838         PlayLevelSoundAction(x, y, ACTION_FILLING);
7839       }
7840       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7841       {
7842         if (!MovDelay[x][y])
7843         {
7844           MovDelay[x][y] = TILEY + 1;
7845
7846           ResetGfxAnimation(x, y);
7847           ResetGfxAnimation(x, y + 1);
7848         }
7849
7850         if (MovDelay[x][y])
7851         {
7852           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7853           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7854
7855           MovDelay[x][y]--;
7856           if (MovDelay[x][y])
7857             return;
7858         }
7859
7860         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7861         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7862         Store[x][y + 1] = Store[x][y];
7863         Store[x][y] = 0;
7864
7865         PlayLevelSoundAction(x, y, ACTION_FILLING);
7866       }
7867     }
7868     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7869              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7870     {
7871       InitMovingField(x, y, MV_DOWN);
7872       started_moving = TRUE;
7873
7874       Tile[x][y] = EL_QUICKSAND_FILLING;
7875       Store[x][y] = element;
7876
7877       PlayLevelSoundAction(x, y, ACTION_FILLING);
7878     }
7879     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7880              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7881     {
7882       InitMovingField(x, y, MV_DOWN);
7883       started_moving = TRUE;
7884
7885       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7886       Store[x][y] = element;
7887
7888       PlayLevelSoundAction(x, y, ACTION_FILLING);
7889     }
7890     else if (element == EL_MAGIC_WALL_FULL)
7891     {
7892       if (IS_FREE(x, y + 1))
7893       {
7894         InitMovingField(x, y, MV_DOWN);
7895         started_moving = TRUE;
7896
7897         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7898         Store[x][y] = EL_CHANGED(Store[x][y]);
7899       }
7900       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7901       {
7902         if (!MovDelay[x][y])
7903           MovDelay[x][y] = TILEY / 4 + 1;
7904
7905         if (MovDelay[x][y])
7906         {
7907           MovDelay[x][y]--;
7908           if (MovDelay[x][y])
7909             return;
7910         }
7911
7912         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7913         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7914         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7915         Store[x][y] = 0;
7916       }
7917     }
7918     else if (element == EL_BD_MAGIC_WALL_FULL)
7919     {
7920       if (IS_FREE(x, y + 1))
7921       {
7922         InitMovingField(x, y, MV_DOWN);
7923         started_moving = TRUE;
7924
7925         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7926         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7927       }
7928       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7929       {
7930         if (!MovDelay[x][y])
7931           MovDelay[x][y] = TILEY / 4 + 1;
7932
7933         if (MovDelay[x][y])
7934         {
7935           MovDelay[x][y]--;
7936           if (MovDelay[x][y])
7937             return;
7938         }
7939
7940         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7941         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7942         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7943         Store[x][y] = 0;
7944       }
7945     }
7946     else if (element == EL_DC_MAGIC_WALL_FULL)
7947     {
7948       if (IS_FREE(x, y + 1))
7949       {
7950         InitMovingField(x, y, MV_DOWN);
7951         started_moving = TRUE;
7952
7953         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7954         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7955       }
7956       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7957       {
7958         if (!MovDelay[x][y])
7959           MovDelay[x][y] = TILEY / 4 + 1;
7960
7961         if (MovDelay[x][y])
7962         {
7963           MovDelay[x][y]--;
7964           if (MovDelay[x][y])
7965             return;
7966         }
7967
7968         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7969         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7970         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7971         Store[x][y] = 0;
7972       }
7973     }
7974     else if ((CAN_PASS_MAGIC_WALL(element) &&
7975               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7976                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7977              (CAN_PASS_DC_MAGIC_WALL(element) &&
7978               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7979
7980     {
7981       InitMovingField(x, y, MV_DOWN);
7982       started_moving = TRUE;
7983
7984       Tile[x][y] =
7985         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7986          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7987          EL_DC_MAGIC_WALL_FILLING);
7988       Store[x][y] = element;
7989     }
7990     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7991     {
7992       SplashAcid(x, y + 1);
7993
7994       InitMovingField(x, y, MV_DOWN);
7995       started_moving = TRUE;
7996
7997       Store[x][y] = EL_ACID;
7998     }
7999     else if (
8000              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8001               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8002              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8003               CAN_FALL(element) && WasJustFalling[x][y] &&
8004               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8005
8006              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8007               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8008               (Tile[x][y + 1] == EL_BLOCKED)))
8009     {
8010       /* this is needed for a special case not covered by calling "Impact()"
8011          from "ContinueMoving()": if an element moves to a tile directly below
8012          another element which was just falling on that tile (which was empty
8013          in the previous frame), the falling element above would just stop
8014          instead of smashing the element below (in previous version, the above
8015          element was just checked for "moving" instead of "falling", resulting
8016          in incorrect smashes caused by horizontal movement of the above
8017          element; also, the case of the player being the element to smash was
8018          simply not covered here... :-/ ) */
8019
8020       CheckCollision[x][y] = 0;
8021       CheckImpact[x][y] = 0;
8022
8023       Impact(x, y);
8024     }
8025     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8026     {
8027       if (MovDir[x][y] == MV_NONE)
8028       {
8029         InitMovingField(x, y, MV_DOWN);
8030         started_moving = TRUE;
8031       }
8032     }
8033     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8034     {
8035       if (WasJustFalling[x][y]) // prevent animation from being restarted
8036         MovDir[x][y] = MV_DOWN;
8037
8038       InitMovingField(x, y, MV_DOWN);
8039       started_moving = TRUE;
8040     }
8041     else if (element == EL_AMOEBA_DROP)
8042     {
8043       Tile[x][y] = EL_AMOEBA_GROWING;
8044       Store[x][y] = EL_AMOEBA_WET;
8045     }
8046     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8047               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8048              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8049              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8050     {
8051       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8052                                 (IS_FREE(x - 1, y + 1) ||
8053                                  Tile[x - 1][y + 1] == EL_ACID));
8054       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8055                                 (IS_FREE(x + 1, y + 1) ||
8056                                  Tile[x + 1][y + 1] == EL_ACID));
8057       boolean can_fall_any  = (can_fall_left || can_fall_right);
8058       boolean can_fall_both = (can_fall_left && can_fall_right);
8059       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8060
8061       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8062       {
8063         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8064           can_fall_right = FALSE;
8065         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8066           can_fall_left = FALSE;
8067         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8068           can_fall_right = FALSE;
8069         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8070           can_fall_left = FALSE;
8071
8072         can_fall_any  = (can_fall_left || can_fall_right);
8073         can_fall_both = FALSE;
8074       }
8075
8076       if (can_fall_both)
8077       {
8078         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8079           can_fall_right = FALSE;       // slip down on left side
8080         else
8081           can_fall_left = !(can_fall_right = RND(2));
8082
8083         can_fall_both = FALSE;
8084       }
8085
8086       if (can_fall_any)
8087       {
8088         // if not determined otherwise, prefer left side for slipping down
8089         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8090         started_moving = TRUE;
8091       }
8092     }
8093     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8094     {
8095       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8096       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8097       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8098       int belt_dir = game.belt_dir[belt_nr];
8099
8100       if ((belt_dir == MV_LEFT  && left_is_free) ||
8101           (belt_dir == MV_RIGHT && right_is_free))
8102       {
8103         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8104
8105         InitMovingField(x, y, belt_dir);
8106         started_moving = TRUE;
8107
8108         Pushed[x][y] = TRUE;
8109         Pushed[nextx][y] = TRUE;
8110
8111         GfxAction[x][y] = ACTION_DEFAULT;
8112       }
8113       else
8114       {
8115         MovDir[x][y] = 0;       // if element was moving, stop it
8116       }
8117     }
8118   }
8119
8120   // not "else if" because of elements that can fall and move (EL_SPRING)
8121   if (CAN_MOVE(element) && !started_moving)
8122   {
8123     int move_pattern = element_info[element].move_pattern;
8124     int newx, newy;
8125
8126     Moving2Blocked(x, y, &newx, &newy);
8127
8128     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8129       return;
8130
8131     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8132         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8133     {
8134       WasJustMoving[x][y] = 0;
8135       CheckCollision[x][y] = 0;
8136
8137       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8138
8139       if (Tile[x][y] != element)        // element has changed
8140         return;
8141     }
8142
8143     if (!MovDelay[x][y])        // start new movement phase
8144     {
8145       // all objects that can change their move direction after each step
8146       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8147
8148       if (element != EL_YAMYAM &&
8149           element != EL_DARK_YAMYAM &&
8150           element != EL_PACMAN &&
8151           !(move_pattern & MV_ANY_DIRECTION) &&
8152           move_pattern != MV_TURNING_LEFT &&
8153           move_pattern != MV_TURNING_RIGHT &&
8154           move_pattern != MV_TURNING_LEFT_RIGHT &&
8155           move_pattern != MV_TURNING_RIGHT_LEFT &&
8156           move_pattern != MV_TURNING_RANDOM)
8157       {
8158         TurnRound(x, y);
8159
8160         if (MovDelay[x][y] && (element == EL_BUG ||
8161                                element == EL_SPACESHIP ||
8162                                element == EL_SP_SNIKSNAK ||
8163                                element == EL_SP_ELECTRON ||
8164                                element == EL_MOLE))
8165           TEST_DrawLevelField(x, y);
8166       }
8167     }
8168
8169     if (MovDelay[x][y])         // wait some time before next movement
8170     {
8171       MovDelay[x][y]--;
8172
8173       if (element == EL_ROBOT ||
8174           element == EL_YAMYAM ||
8175           element == EL_DARK_YAMYAM)
8176       {
8177         DrawLevelElementAnimationIfNeeded(x, y, element);
8178         PlayLevelSoundAction(x, y, ACTION_WAITING);
8179       }
8180       else if (element == EL_SP_ELECTRON)
8181         DrawLevelElementAnimationIfNeeded(x, y, element);
8182       else if (element == EL_DRAGON)
8183       {
8184         int i;
8185         int dir = MovDir[x][y];
8186         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8187         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8188         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8189                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8190                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8191                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8192         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8193
8194         GfxAction[x][y] = ACTION_ATTACKING;
8195
8196         if (IS_PLAYER(x, y))
8197           DrawPlayerField(x, y);
8198         else
8199           TEST_DrawLevelField(x, y);
8200
8201         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8202
8203         for (i = 1; i <= 3; i++)
8204         {
8205           int xx = x + i * dx;
8206           int yy = y + i * dy;
8207           int sx = SCREENX(xx);
8208           int sy = SCREENY(yy);
8209           int flame_graphic = graphic + (i - 1);
8210
8211           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8212             break;
8213
8214           if (MovDelay[x][y])
8215           {
8216             int flamed = MovingOrBlocked2Element(xx, yy);
8217
8218             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8219               Bang(xx, yy);
8220             else
8221               RemoveMovingField(xx, yy);
8222
8223             ChangeDelay[xx][yy] = 0;
8224
8225             Tile[xx][yy] = EL_FLAMES;
8226
8227             if (IN_SCR_FIELD(sx, sy))
8228             {
8229               TEST_DrawLevelFieldCrumbled(xx, yy);
8230               DrawGraphic(sx, sy, flame_graphic, frame);
8231             }
8232           }
8233           else
8234           {
8235             if (Tile[xx][yy] == EL_FLAMES)
8236               Tile[xx][yy] = EL_EMPTY;
8237             TEST_DrawLevelField(xx, yy);
8238           }
8239         }
8240       }
8241
8242       if (MovDelay[x][y])       // element still has to wait some time
8243       {
8244         PlayLevelSoundAction(x, y, ACTION_WAITING);
8245
8246         return;
8247       }
8248     }
8249
8250     // now make next step
8251
8252     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8253
8254     if (DONT_COLLIDE_WITH(element) &&
8255         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8256         !PLAYER_ENEMY_PROTECTED(newx, newy))
8257     {
8258       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8259
8260       return;
8261     }
8262
8263     else if (CAN_MOVE_INTO_ACID(element) &&
8264              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8265              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8266              (MovDir[x][y] == MV_DOWN ||
8267               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8268     {
8269       SplashAcid(newx, newy);
8270       Store[x][y] = EL_ACID;
8271     }
8272     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8273     {
8274       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8275           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8276           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8277           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8278       {
8279         RemoveField(x, y);
8280         TEST_DrawLevelField(x, y);
8281
8282         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8283         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8284           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8285
8286         game.friends_still_needed--;
8287         if (!game.friends_still_needed &&
8288             !game.GameOver &&
8289             game.all_players_gone)
8290           LevelSolved();
8291
8292         return;
8293       }
8294       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8295       {
8296         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8297           TEST_DrawLevelField(newx, newy);
8298         else
8299           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8300       }
8301       else if (!IS_FREE(newx, newy))
8302       {
8303         GfxAction[x][y] = ACTION_WAITING;
8304
8305         if (IS_PLAYER(x, y))
8306           DrawPlayerField(x, y);
8307         else
8308           TEST_DrawLevelField(x, y);
8309
8310         return;
8311       }
8312     }
8313     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8314     {
8315       if (IS_FOOD_PIG(Tile[newx][newy]))
8316       {
8317         if (IS_MOVING(newx, newy))
8318           RemoveMovingField(newx, newy);
8319         else
8320         {
8321           Tile[newx][newy] = EL_EMPTY;
8322           TEST_DrawLevelField(newx, newy);
8323         }
8324
8325         PlayLevelSound(x, y, SND_PIG_DIGGING);
8326       }
8327       else if (!IS_FREE(newx, newy))
8328       {
8329         if (IS_PLAYER(x, y))
8330           DrawPlayerField(x, y);
8331         else
8332           TEST_DrawLevelField(x, y);
8333
8334         return;
8335       }
8336     }
8337     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8338     {
8339       if (Store[x][y] != EL_EMPTY)
8340       {
8341         boolean can_clone = FALSE;
8342         int xx, yy;
8343
8344         // check if element to clone is still there
8345         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8346         {
8347           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8348           {
8349             can_clone = TRUE;
8350
8351             break;
8352           }
8353         }
8354
8355         // cannot clone or target field not free anymore -- do not clone
8356         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8357           Store[x][y] = EL_EMPTY;
8358       }
8359
8360       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8361       {
8362         if (IS_MV_DIAGONAL(MovDir[x][y]))
8363         {
8364           int diagonal_move_dir = MovDir[x][y];
8365           int stored = Store[x][y];
8366           int change_delay = 8;
8367           int graphic;
8368
8369           // android is moving diagonally
8370
8371           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8372
8373           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8374           GfxElement[x][y] = EL_EMC_ANDROID;
8375           GfxAction[x][y] = ACTION_SHRINKING;
8376           GfxDir[x][y] = diagonal_move_dir;
8377           ChangeDelay[x][y] = change_delay;
8378
8379           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8380                                    GfxDir[x][y]);
8381
8382           DrawLevelGraphicAnimation(x, y, graphic);
8383           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8384
8385           if (Tile[newx][newy] == EL_ACID)
8386           {
8387             SplashAcid(newx, newy);
8388
8389             return;
8390           }
8391
8392           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8393
8394           Store[newx][newy] = EL_EMC_ANDROID;
8395           GfxElement[newx][newy] = EL_EMC_ANDROID;
8396           GfxAction[newx][newy] = ACTION_GROWING;
8397           GfxDir[newx][newy] = diagonal_move_dir;
8398           ChangeDelay[newx][newy] = change_delay;
8399
8400           graphic = el_act_dir2img(GfxElement[newx][newy],
8401                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8402
8403           DrawLevelGraphicAnimation(newx, newy, graphic);
8404           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8405
8406           return;
8407         }
8408         else
8409         {
8410           Tile[newx][newy] = EL_EMPTY;
8411           TEST_DrawLevelField(newx, newy);
8412
8413           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8414         }
8415       }
8416       else if (!IS_FREE(newx, newy))
8417       {
8418         return;
8419       }
8420     }
8421     else if (IS_CUSTOM_ELEMENT(element) &&
8422              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8423     {
8424       if (!DigFieldByCE(newx, newy, element))
8425         return;
8426
8427       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8428       {
8429         RunnerVisit[x][y] = FrameCounter;
8430         PlayerVisit[x][y] /= 8;         // expire player visit path
8431       }
8432     }
8433     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8434     {
8435       if (!IS_FREE(newx, newy))
8436       {
8437         if (IS_PLAYER(x, y))
8438           DrawPlayerField(x, y);
8439         else
8440           TEST_DrawLevelField(x, y);
8441
8442         return;
8443       }
8444       else
8445       {
8446         boolean wanna_flame = !RND(10);
8447         int dx = newx - x, dy = newy - y;
8448         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8449         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8450         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8451                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8452         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8453                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8454
8455         if ((wanna_flame ||
8456              IS_CLASSIC_ENEMY(element1) ||
8457              IS_CLASSIC_ENEMY(element2)) &&
8458             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8459             element1 != EL_FLAMES && element2 != EL_FLAMES)
8460         {
8461           ResetGfxAnimation(x, y);
8462           GfxAction[x][y] = ACTION_ATTACKING;
8463
8464           if (IS_PLAYER(x, y))
8465             DrawPlayerField(x, y);
8466           else
8467             TEST_DrawLevelField(x, y);
8468
8469           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8470
8471           MovDelay[x][y] = 50;
8472
8473           Tile[newx][newy] = EL_FLAMES;
8474           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8475             Tile[newx1][newy1] = EL_FLAMES;
8476           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8477             Tile[newx2][newy2] = EL_FLAMES;
8478
8479           return;
8480         }
8481       }
8482     }
8483     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8484              Tile[newx][newy] == EL_DIAMOND)
8485     {
8486       if (IS_MOVING(newx, newy))
8487         RemoveMovingField(newx, newy);
8488       else
8489       {
8490         Tile[newx][newy] = EL_EMPTY;
8491         TEST_DrawLevelField(newx, newy);
8492       }
8493
8494       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8495     }
8496     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8497              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8498     {
8499       if (AmoebaNr[newx][newy])
8500       {
8501         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8502         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8503             Tile[newx][newy] == EL_BD_AMOEBA)
8504           AmoebaCnt[AmoebaNr[newx][newy]]--;
8505       }
8506
8507       if (IS_MOVING(newx, newy))
8508       {
8509         RemoveMovingField(newx, newy);
8510       }
8511       else
8512       {
8513         Tile[newx][newy] = EL_EMPTY;
8514         TEST_DrawLevelField(newx, newy);
8515       }
8516
8517       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8518     }
8519     else if ((element == EL_PACMAN || element == EL_MOLE)
8520              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8521     {
8522       if (AmoebaNr[newx][newy])
8523       {
8524         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8525         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8526             Tile[newx][newy] == EL_BD_AMOEBA)
8527           AmoebaCnt[AmoebaNr[newx][newy]]--;
8528       }
8529
8530       if (element == EL_MOLE)
8531       {
8532         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8533         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8534
8535         ResetGfxAnimation(x, y);
8536         GfxAction[x][y] = ACTION_DIGGING;
8537         TEST_DrawLevelField(x, y);
8538
8539         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8540
8541         return;                         // wait for shrinking amoeba
8542       }
8543       else      // element == EL_PACMAN
8544       {
8545         Tile[newx][newy] = EL_EMPTY;
8546         TEST_DrawLevelField(newx, newy);
8547         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8548       }
8549     }
8550     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8551              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8552               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8553     {
8554       // wait for shrinking amoeba to completely disappear
8555       return;
8556     }
8557     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8558     {
8559       // object was running against a wall
8560
8561       TurnRound(x, y);
8562
8563       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8564         DrawLevelElementAnimation(x, y, element);
8565
8566       if (DONT_TOUCH(element))
8567         TestIfBadThingTouchesPlayer(x, y);
8568
8569       return;
8570     }
8571
8572     InitMovingField(x, y, MovDir[x][y]);
8573
8574     PlayLevelSoundAction(x, y, ACTION_MOVING);
8575   }
8576
8577   if (MovDir[x][y])
8578     ContinueMoving(x, y);
8579 }
8580
8581 void ContinueMoving(int x, int y)
8582 {
8583   int element = Tile[x][y];
8584   struct ElementInfo *ei = &element_info[element];
8585   int direction = MovDir[x][y];
8586   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8587   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8588   int newx = x + dx, newy = y + dy;
8589   int stored = Store[x][y];
8590   int stored_new = Store[newx][newy];
8591   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8592   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8593   boolean last_line = (newy == lev_fieldy - 1);
8594   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8595
8596   if (pushed_by_player)         // special case: moving object pushed by player
8597   {
8598     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8599   }
8600   else if (use_step_delay)      // special case: moving object has step delay
8601   {
8602     if (!MovDelay[x][y])
8603       MovPos[x][y] += getElementMoveStepsize(x, y);
8604
8605     if (MovDelay[x][y])
8606       MovDelay[x][y]--;
8607     else
8608       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8609
8610     if (MovDelay[x][y])
8611     {
8612       TEST_DrawLevelField(x, y);
8613
8614       return;   // element is still waiting
8615     }
8616   }
8617   else                          // normal case: generically moving object
8618   {
8619     MovPos[x][y] += getElementMoveStepsize(x, y);
8620   }
8621
8622   if (ABS(MovPos[x][y]) < TILEX)
8623   {
8624     TEST_DrawLevelField(x, y);
8625
8626     return;     // element is still moving
8627   }
8628
8629   // element reached destination field
8630
8631   Tile[x][y] = EL_EMPTY;
8632   Tile[newx][newy] = element;
8633   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8634
8635   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8636   {
8637     element = Tile[newx][newy] = EL_ACID;
8638   }
8639   else if (element == EL_MOLE)
8640   {
8641     Tile[x][y] = EL_SAND;
8642
8643     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8644   }
8645   else if (element == EL_QUICKSAND_FILLING)
8646   {
8647     element = Tile[newx][newy] = get_next_element(element);
8648     Store[newx][newy] = Store[x][y];
8649   }
8650   else if (element == EL_QUICKSAND_EMPTYING)
8651   {
8652     Tile[x][y] = get_next_element(element);
8653     element = Tile[newx][newy] = Store[x][y];
8654   }
8655   else if (element == EL_QUICKSAND_FAST_FILLING)
8656   {
8657     element = Tile[newx][newy] = get_next_element(element);
8658     Store[newx][newy] = Store[x][y];
8659   }
8660   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8661   {
8662     Tile[x][y] = get_next_element(element);
8663     element = Tile[newx][newy] = Store[x][y];
8664   }
8665   else if (element == EL_MAGIC_WALL_FILLING)
8666   {
8667     element = Tile[newx][newy] = get_next_element(element);
8668     if (!game.magic_wall_active)
8669       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8670     Store[newx][newy] = Store[x][y];
8671   }
8672   else if (element == EL_MAGIC_WALL_EMPTYING)
8673   {
8674     Tile[x][y] = get_next_element(element);
8675     if (!game.magic_wall_active)
8676       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8677     element = Tile[newx][newy] = Store[x][y];
8678
8679     InitField(newx, newy, FALSE);
8680   }
8681   else if (element == EL_BD_MAGIC_WALL_FILLING)
8682   {
8683     element = Tile[newx][newy] = get_next_element(element);
8684     if (!game.magic_wall_active)
8685       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8686     Store[newx][newy] = Store[x][y];
8687   }
8688   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8689   {
8690     Tile[x][y] = get_next_element(element);
8691     if (!game.magic_wall_active)
8692       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8693     element = Tile[newx][newy] = Store[x][y];
8694
8695     InitField(newx, newy, FALSE);
8696   }
8697   else if (element == EL_DC_MAGIC_WALL_FILLING)
8698   {
8699     element = Tile[newx][newy] = get_next_element(element);
8700     if (!game.magic_wall_active)
8701       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8702     Store[newx][newy] = Store[x][y];
8703   }
8704   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8705   {
8706     Tile[x][y] = get_next_element(element);
8707     if (!game.magic_wall_active)
8708       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8709     element = Tile[newx][newy] = Store[x][y];
8710
8711     InitField(newx, newy, FALSE);
8712   }
8713   else if (element == EL_AMOEBA_DROPPING)
8714   {
8715     Tile[x][y] = get_next_element(element);
8716     element = Tile[newx][newy] = Store[x][y];
8717   }
8718   else if (element == EL_SOKOBAN_OBJECT)
8719   {
8720     if (Back[x][y])
8721       Tile[x][y] = Back[x][y];
8722
8723     if (Back[newx][newy])
8724       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8725
8726     Back[x][y] = Back[newx][newy] = 0;
8727   }
8728
8729   Store[x][y] = EL_EMPTY;
8730   MovPos[x][y] = 0;
8731   MovDir[x][y] = 0;
8732   MovDelay[x][y] = 0;
8733
8734   MovDelay[newx][newy] = 0;
8735
8736   if (CAN_CHANGE_OR_HAS_ACTION(element))
8737   {
8738     // copy element change control values to new field
8739     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8740     ChangePage[newx][newy]  = ChangePage[x][y];
8741     ChangeCount[newx][newy] = ChangeCount[x][y];
8742     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8743   }
8744
8745   CustomValue[newx][newy] = CustomValue[x][y];
8746
8747   ChangeDelay[x][y] = 0;
8748   ChangePage[x][y] = -1;
8749   ChangeCount[x][y] = 0;
8750   ChangeEvent[x][y] = -1;
8751
8752   CustomValue[x][y] = 0;
8753
8754   // copy animation control values to new field
8755   GfxFrame[newx][newy]  = GfxFrame[x][y];
8756   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8757   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8758   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8759
8760   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8761
8762   // some elements can leave other elements behind after moving
8763   if (ei->move_leave_element != EL_EMPTY &&
8764       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8765       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8766   {
8767     int move_leave_element = ei->move_leave_element;
8768
8769     // this makes it possible to leave the removed element again
8770     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8771       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8772
8773     Tile[x][y] = move_leave_element;
8774
8775     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8776       MovDir[x][y] = direction;
8777
8778     InitField(x, y, FALSE);
8779
8780     if (GFX_CRUMBLED(Tile[x][y]))
8781       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8782
8783     if (IS_PLAYER_ELEMENT(move_leave_element))
8784       RelocatePlayer(x, y, move_leave_element);
8785   }
8786
8787   // do this after checking for left-behind element
8788   ResetGfxAnimation(x, y);      // reset animation values for old field
8789
8790   if (!CAN_MOVE(element) ||
8791       (CAN_FALL(element) && direction == MV_DOWN &&
8792        (element == EL_SPRING ||
8793         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8794         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8795     GfxDir[x][y] = MovDir[newx][newy] = 0;
8796
8797   TEST_DrawLevelField(x, y);
8798   TEST_DrawLevelField(newx, newy);
8799
8800   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8801
8802   // prevent pushed element from moving on in pushed direction
8803   if (pushed_by_player && CAN_MOVE(element) &&
8804       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8805       !(element_info[element].move_pattern & direction))
8806     TurnRound(newx, newy);
8807
8808   // prevent elements on conveyor belt from moving on in last direction
8809   if (pushed_by_conveyor && CAN_FALL(element) &&
8810       direction & MV_HORIZONTAL)
8811     MovDir[newx][newy] = 0;
8812
8813   if (!pushed_by_player)
8814   {
8815     int nextx = newx + dx, nexty = newy + dy;
8816     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8817
8818     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8819
8820     if (CAN_FALL(element) && direction == MV_DOWN)
8821       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8822
8823     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8824       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8825
8826     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8827       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8828   }
8829
8830   if (DONT_TOUCH(element))      // object may be nasty to player or others
8831   {
8832     TestIfBadThingTouchesPlayer(newx, newy);
8833     TestIfBadThingTouchesFriend(newx, newy);
8834
8835     if (!IS_CUSTOM_ELEMENT(element))
8836       TestIfBadThingTouchesOtherBadThing(newx, newy);
8837   }
8838   else if (element == EL_PENGUIN)
8839     TestIfFriendTouchesBadThing(newx, newy);
8840
8841   if (DONT_GET_HIT_BY(element))
8842   {
8843     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8844   }
8845
8846   // give the player one last chance (one more frame) to move away
8847   if (CAN_FALL(element) && direction == MV_DOWN &&
8848       (last_line || (!IS_FREE(x, newy + 1) &&
8849                      (!IS_PLAYER(x, newy + 1) ||
8850                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8851     Impact(x, newy);
8852
8853   if (pushed_by_player && !game.use_change_when_pushing_bug)
8854   {
8855     int push_side = MV_DIR_OPPOSITE(direction);
8856     struct PlayerInfo *player = PLAYERINFO(x, y);
8857
8858     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8859                                player->index_bit, push_side);
8860     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8861                                         player->index_bit, push_side);
8862   }
8863
8864   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8865     MovDelay[newx][newy] = 1;
8866
8867   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8868
8869   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8870   TestIfElementHitsCustomElement(newx, newy, direction);
8871   TestIfPlayerTouchesCustomElement(newx, newy);
8872   TestIfElementTouchesCustomElement(newx, newy);
8873
8874   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8875       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8876     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8877                              MV_DIR_OPPOSITE(direction));
8878 }
8879
8880 int AmoebaNeighbourNr(int ax, int ay)
8881 {
8882   int i;
8883   int element = Tile[ax][ay];
8884   int group_nr = 0;
8885   static int xy[4][2] =
8886   {
8887     { 0, -1 },
8888     { -1, 0 },
8889     { +1, 0 },
8890     { 0, +1 }
8891   };
8892
8893   for (i = 0; i < NUM_DIRECTIONS; i++)
8894   {
8895     int x = ax + xy[i][0];
8896     int y = ay + xy[i][1];
8897
8898     if (!IN_LEV_FIELD(x, y))
8899       continue;
8900
8901     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8902       group_nr = AmoebaNr[x][y];
8903   }
8904
8905   return group_nr;
8906 }
8907
8908 static void AmoebaMerge(int ax, int ay)
8909 {
8910   int i, x, y, xx, yy;
8911   int new_group_nr = AmoebaNr[ax][ay];
8912   static int xy[4][2] =
8913   {
8914     { 0, -1 },
8915     { -1, 0 },
8916     { +1, 0 },
8917     { 0, +1 }
8918   };
8919
8920   if (new_group_nr == 0)
8921     return;
8922
8923   for (i = 0; i < NUM_DIRECTIONS; i++)
8924   {
8925     x = ax + xy[i][0];
8926     y = ay + xy[i][1];
8927
8928     if (!IN_LEV_FIELD(x, y))
8929       continue;
8930
8931     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8932          Tile[x][y] == EL_BD_AMOEBA ||
8933          Tile[x][y] == EL_AMOEBA_DEAD) &&
8934         AmoebaNr[x][y] != new_group_nr)
8935     {
8936       int old_group_nr = AmoebaNr[x][y];
8937
8938       if (old_group_nr == 0)
8939         return;
8940
8941       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8942       AmoebaCnt[old_group_nr] = 0;
8943       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8944       AmoebaCnt2[old_group_nr] = 0;
8945
8946       SCAN_PLAYFIELD(xx, yy)
8947       {
8948         if (AmoebaNr[xx][yy] == old_group_nr)
8949           AmoebaNr[xx][yy] = new_group_nr;
8950       }
8951     }
8952   }
8953 }
8954
8955 void AmoebaToDiamond(int ax, int ay)
8956 {
8957   int i, x, y;
8958
8959   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8960   {
8961     int group_nr = AmoebaNr[ax][ay];
8962
8963 #ifdef DEBUG
8964     if (group_nr == 0)
8965     {
8966       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8967       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8968
8969       return;
8970     }
8971 #endif
8972
8973     SCAN_PLAYFIELD(x, y)
8974     {
8975       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8976       {
8977         AmoebaNr[x][y] = 0;
8978         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8979       }
8980     }
8981
8982     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8983                             SND_AMOEBA_TURNING_TO_GEM :
8984                             SND_AMOEBA_TURNING_TO_ROCK));
8985     Bang(ax, ay);
8986   }
8987   else
8988   {
8989     static int xy[4][2] =
8990     {
8991       { 0, -1 },
8992       { -1, 0 },
8993       { +1, 0 },
8994       { 0, +1 }
8995     };
8996
8997     for (i = 0; i < NUM_DIRECTIONS; i++)
8998     {
8999       x = ax + xy[i][0];
9000       y = ay + xy[i][1];
9001
9002       if (!IN_LEV_FIELD(x, y))
9003         continue;
9004
9005       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9006       {
9007         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9008                               SND_AMOEBA_TURNING_TO_GEM :
9009                               SND_AMOEBA_TURNING_TO_ROCK));
9010         Bang(x, y);
9011       }
9012     }
9013   }
9014 }
9015
9016 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9017 {
9018   int x, y;
9019   int group_nr = AmoebaNr[ax][ay];
9020   boolean done = FALSE;
9021
9022 #ifdef DEBUG
9023   if (group_nr == 0)
9024   {
9025     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9026     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9027
9028     return;
9029   }
9030 #endif
9031
9032   SCAN_PLAYFIELD(x, y)
9033   {
9034     if (AmoebaNr[x][y] == group_nr &&
9035         (Tile[x][y] == EL_AMOEBA_DEAD ||
9036          Tile[x][y] == EL_BD_AMOEBA ||
9037          Tile[x][y] == EL_AMOEBA_GROWING))
9038     {
9039       AmoebaNr[x][y] = 0;
9040       Tile[x][y] = new_element;
9041       InitField(x, y, FALSE);
9042       TEST_DrawLevelField(x, y);
9043       done = TRUE;
9044     }
9045   }
9046
9047   if (done)
9048     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9049                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9050                             SND_BD_AMOEBA_TURNING_TO_GEM));
9051 }
9052
9053 static void AmoebaGrowing(int x, int y)
9054 {
9055   static unsigned int sound_delay = 0;
9056   static unsigned int sound_delay_value = 0;
9057
9058   if (!MovDelay[x][y])          // start new growing cycle
9059   {
9060     MovDelay[x][y] = 7;
9061
9062     if (DelayReached(&sound_delay, sound_delay_value))
9063     {
9064       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9065       sound_delay_value = 30;
9066     }
9067   }
9068
9069   if (MovDelay[x][y])           // wait some time before growing bigger
9070   {
9071     MovDelay[x][y]--;
9072     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9073     {
9074       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9075                                            6 - MovDelay[x][y]);
9076
9077       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9078     }
9079
9080     if (!MovDelay[x][y])
9081     {
9082       Tile[x][y] = Store[x][y];
9083       Store[x][y] = 0;
9084       TEST_DrawLevelField(x, y);
9085     }
9086   }
9087 }
9088
9089 static void AmoebaShrinking(int x, int y)
9090 {
9091   static unsigned int sound_delay = 0;
9092   static unsigned int sound_delay_value = 0;
9093
9094   if (!MovDelay[x][y])          // start new shrinking cycle
9095   {
9096     MovDelay[x][y] = 7;
9097
9098     if (DelayReached(&sound_delay, sound_delay_value))
9099       sound_delay_value = 30;
9100   }
9101
9102   if (MovDelay[x][y])           // wait some time before shrinking
9103   {
9104     MovDelay[x][y]--;
9105     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9106     {
9107       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9108                                            6 - MovDelay[x][y]);
9109
9110       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9111     }
9112
9113     if (!MovDelay[x][y])
9114     {
9115       Tile[x][y] = EL_EMPTY;
9116       TEST_DrawLevelField(x, y);
9117
9118       // don't let mole enter this field in this cycle;
9119       // (give priority to objects falling to this field from above)
9120       Stop[x][y] = TRUE;
9121     }
9122   }
9123 }
9124
9125 static void AmoebaReproduce(int ax, int ay)
9126 {
9127   int i;
9128   int element = Tile[ax][ay];
9129   int graphic = el2img(element);
9130   int newax = ax, neway = ay;
9131   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9132   static int xy[4][2] =
9133   {
9134     { 0, -1 },
9135     { -1, 0 },
9136     { +1, 0 },
9137     { 0, +1 }
9138   };
9139
9140   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9141   {
9142     Tile[ax][ay] = EL_AMOEBA_DEAD;
9143     TEST_DrawLevelField(ax, ay);
9144     return;
9145   }
9146
9147   if (IS_ANIMATED(graphic))
9148     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9149
9150   if (!MovDelay[ax][ay])        // start making new amoeba field
9151     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9152
9153   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9154   {
9155     MovDelay[ax][ay]--;
9156     if (MovDelay[ax][ay])
9157       return;
9158   }
9159
9160   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9161   {
9162     int start = RND(4);
9163     int x = ax + xy[start][0];
9164     int y = ay + xy[start][1];
9165
9166     if (!IN_LEV_FIELD(x, y))
9167       return;
9168
9169     if (IS_FREE(x, y) ||
9170         CAN_GROW_INTO(Tile[x][y]) ||
9171         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9172         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9173     {
9174       newax = x;
9175       neway = y;
9176     }
9177
9178     if (newax == ax && neway == ay)
9179       return;
9180   }
9181   else                          // normal or "filled" (BD style) amoeba
9182   {
9183     int start = RND(4);
9184     boolean waiting_for_player = FALSE;
9185
9186     for (i = 0; i < NUM_DIRECTIONS; i++)
9187     {
9188       int j = (start + i) % 4;
9189       int x = ax + xy[j][0];
9190       int y = ay + xy[j][1];
9191
9192       if (!IN_LEV_FIELD(x, y))
9193         continue;
9194
9195       if (IS_FREE(x, y) ||
9196           CAN_GROW_INTO(Tile[x][y]) ||
9197           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9198           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9199       {
9200         newax = x;
9201         neway = y;
9202         break;
9203       }
9204       else if (IS_PLAYER(x, y))
9205         waiting_for_player = TRUE;
9206     }
9207
9208     if (newax == ax && neway == ay)             // amoeba cannot grow
9209     {
9210       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9211       {
9212         Tile[ax][ay] = EL_AMOEBA_DEAD;
9213         TEST_DrawLevelField(ax, ay);
9214         AmoebaCnt[AmoebaNr[ax][ay]]--;
9215
9216         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9217         {
9218           if (element == EL_AMOEBA_FULL)
9219             AmoebaToDiamond(ax, ay);
9220           else if (element == EL_BD_AMOEBA)
9221             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9222         }
9223       }
9224       return;
9225     }
9226     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9227     {
9228       // amoeba gets larger by growing in some direction
9229
9230       int new_group_nr = AmoebaNr[ax][ay];
9231
9232 #ifdef DEBUG
9233   if (new_group_nr == 0)
9234   {
9235     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9236           newax, neway);
9237     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9238
9239     return;
9240   }
9241 #endif
9242
9243       AmoebaNr[newax][neway] = new_group_nr;
9244       AmoebaCnt[new_group_nr]++;
9245       AmoebaCnt2[new_group_nr]++;
9246
9247       // if amoeba touches other amoeba(s) after growing, unify them
9248       AmoebaMerge(newax, neway);
9249
9250       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9251       {
9252         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9253         return;
9254       }
9255     }
9256   }
9257
9258   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9259       (neway == lev_fieldy - 1 && newax != ax))
9260   {
9261     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9262     Store[newax][neway] = element;
9263   }
9264   else if (neway == ay || element == EL_EMC_DRIPPER)
9265   {
9266     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9267
9268     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9269   }
9270   else
9271   {
9272     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9273     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9274     Store[ax][ay] = EL_AMOEBA_DROP;
9275     ContinueMoving(ax, ay);
9276     return;
9277   }
9278
9279   TEST_DrawLevelField(newax, neway);
9280 }
9281
9282 static void Life(int ax, int ay)
9283 {
9284   int x1, y1, x2, y2;
9285   int life_time = 40;
9286   int element = Tile[ax][ay];
9287   int graphic = el2img(element);
9288   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9289                          level.biomaze);
9290   boolean changed = FALSE;
9291
9292   if (IS_ANIMATED(graphic))
9293     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9294
9295   if (Stop[ax][ay])
9296     return;
9297
9298   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9299     MovDelay[ax][ay] = life_time;
9300
9301   if (MovDelay[ax][ay])         // wait some time before next cycle
9302   {
9303     MovDelay[ax][ay]--;
9304     if (MovDelay[ax][ay])
9305       return;
9306   }
9307
9308   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9309   {
9310     int xx = ax+x1, yy = ay+y1;
9311     int old_element = Tile[xx][yy];
9312     int num_neighbours = 0;
9313
9314     if (!IN_LEV_FIELD(xx, yy))
9315       continue;
9316
9317     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9318     {
9319       int x = xx+x2, y = yy+y2;
9320
9321       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9322         continue;
9323
9324       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9325       boolean is_neighbour = FALSE;
9326
9327       if (level.use_life_bugs)
9328         is_neighbour =
9329           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9330            (IS_FREE(x, y)                             &&  Stop[x][y]));
9331       else
9332         is_neighbour =
9333           (Last[x][y] == element || is_player_cell);
9334
9335       if (is_neighbour)
9336         num_neighbours++;
9337     }
9338
9339     boolean is_free = FALSE;
9340
9341     if (level.use_life_bugs)
9342       is_free = (IS_FREE(xx, yy));
9343     else
9344       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9345
9346     if (xx == ax && yy == ay)           // field in the middle
9347     {
9348       if (num_neighbours < life_parameter[0] ||
9349           num_neighbours > life_parameter[1])
9350       {
9351         Tile[xx][yy] = EL_EMPTY;
9352         if (Tile[xx][yy] != old_element)
9353           TEST_DrawLevelField(xx, yy);
9354         Stop[xx][yy] = TRUE;
9355         changed = TRUE;
9356       }
9357     }
9358     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9359     {                                   // free border field
9360       if (num_neighbours >= life_parameter[2] &&
9361           num_neighbours <= life_parameter[3])
9362       {
9363         Tile[xx][yy] = element;
9364         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9365         if (Tile[xx][yy] != old_element)
9366           TEST_DrawLevelField(xx, yy);
9367         Stop[xx][yy] = TRUE;
9368         changed = TRUE;
9369       }
9370     }
9371   }
9372
9373   if (changed)
9374     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9375                    SND_GAME_OF_LIFE_GROWING);
9376 }
9377
9378 static void InitRobotWheel(int x, int y)
9379 {
9380   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9381 }
9382
9383 static void RunRobotWheel(int x, int y)
9384 {
9385   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9386 }
9387
9388 static void StopRobotWheel(int x, int y)
9389 {
9390   if (game.robot_wheel_x == x &&
9391       game.robot_wheel_y == y)
9392   {
9393     game.robot_wheel_x = -1;
9394     game.robot_wheel_y = -1;
9395     game.robot_wheel_active = FALSE;
9396   }
9397 }
9398
9399 static void InitTimegateWheel(int x, int y)
9400 {
9401   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9402 }
9403
9404 static void RunTimegateWheel(int x, int y)
9405 {
9406   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9407 }
9408
9409 static void InitMagicBallDelay(int x, int y)
9410 {
9411   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9412 }
9413
9414 static void ActivateMagicBall(int bx, int by)
9415 {
9416   int x, y;
9417
9418   if (level.ball_random)
9419   {
9420     int pos_border = RND(8);    // select one of the eight border elements
9421     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9422     int xx = pos_content % 3;
9423     int yy = pos_content / 3;
9424
9425     x = bx - 1 + xx;
9426     y = by - 1 + yy;
9427
9428     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9429       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9430   }
9431   else
9432   {
9433     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9434     {
9435       int xx = x - bx + 1;
9436       int yy = y - by + 1;
9437
9438       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9439         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9440     }
9441   }
9442
9443   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9444 }
9445
9446 static void CheckExit(int x, int y)
9447 {
9448   if (game.gems_still_needed > 0 ||
9449       game.sokoban_fields_still_needed > 0 ||
9450       game.sokoban_objects_still_needed > 0 ||
9451       game.lights_still_needed > 0)
9452   {
9453     int element = Tile[x][y];
9454     int graphic = el2img(element);
9455
9456     if (IS_ANIMATED(graphic))
9457       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9458
9459     return;
9460   }
9461
9462   // do not re-open exit door closed after last player
9463   if (game.all_players_gone)
9464     return;
9465
9466   Tile[x][y] = EL_EXIT_OPENING;
9467
9468   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9469 }
9470
9471 static void CheckExitEM(int x, int y)
9472 {
9473   if (game.gems_still_needed > 0 ||
9474       game.sokoban_fields_still_needed > 0 ||
9475       game.sokoban_objects_still_needed > 0 ||
9476       game.lights_still_needed > 0)
9477   {
9478     int element = Tile[x][y];
9479     int graphic = el2img(element);
9480
9481     if (IS_ANIMATED(graphic))
9482       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9483
9484     return;
9485   }
9486
9487   // do not re-open exit door closed after last player
9488   if (game.all_players_gone)
9489     return;
9490
9491   Tile[x][y] = EL_EM_EXIT_OPENING;
9492
9493   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9494 }
9495
9496 static void CheckExitSteel(int x, int y)
9497 {
9498   if (game.gems_still_needed > 0 ||
9499       game.sokoban_fields_still_needed > 0 ||
9500       game.sokoban_objects_still_needed > 0 ||
9501       game.lights_still_needed > 0)
9502   {
9503     int element = Tile[x][y];
9504     int graphic = el2img(element);
9505
9506     if (IS_ANIMATED(graphic))
9507       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9508
9509     return;
9510   }
9511
9512   // do not re-open exit door closed after last player
9513   if (game.all_players_gone)
9514     return;
9515
9516   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9517
9518   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9519 }
9520
9521 static void CheckExitSteelEM(int x, int y)
9522 {
9523   if (game.gems_still_needed > 0 ||
9524       game.sokoban_fields_still_needed > 0 ||
9525       game.sokoban_objects_still_needed > 0 ||
9526       game.lights_still_needed > 0)
9527   {
9528     int element = Tile[x][y];
9529     int graphic = el2img(element);
9530
9531     if (IS_ANIMATED(graphic))
9532       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9533
9534     return;
9535   }
9536
9537   // do not re-open exit door closed after last player
9538   if (game.all_players_gone)
9539     return;
9540
9541   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9542
9543   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9544 }
9545
9546 static void CheckExitSP(int x, int y)
9547 {
9548   if (game.gems_still_needed > 0)
9549   {
9550     int element = Tile[x][y];
9551     int graphic = el2img(element);
9552
9553     if (IS_ANIMATED(graphic))
9554       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9555
9556     return;
9557   }
9558
9559   // do not re-open exit door closed after last player
9560   if (game.all_players_gone)
9561     return;
9562
9563   Tile[x][y] = EL_SP_EXIT_OPENING;
9564
9565   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9566 }
9567
9568 static void CloseAllOpenTimegates(void)
9569 {
9570   int x, y;
9571
9572   SCAN_PLAYFIELD(x, y)
9573   {
9574     int element = Tile[x][y];
9575
9576     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9577     {
9578       Tile[x][y] = EL_TIMEGATE_CLOSING;
9579
9580       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9581     }
9582   }
9583 }
9584
9585 static void DrawTwinkleOnField(int x, int y)
9586 {
9587   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9588     return;
9589
9590   if (Tile[x][y] == EL_BD_DIAMOND)
9591     return;
9592
9593   if (MovDelay[x][y] == 0)      // next animation frame
9594     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9595
9596   if (MovDelay[x][y] != 0)      // wait some time before next frame
9597   {
9598     MovDelay[x][y]--;
9599
9600     DrawLevelElementAnimation(x, y, Tile[x][y]);
9601
9602     if (MovDelay[x][y] != 0)
9603     {
9604       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9605                                            10 - MovDelay[x][y]);
9606
9607       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9608     }
9609   }
9610 }
9611
9612 static void MauerWaechst(int x, int y)
9613 {
9614   int delay = 6;
9615
9616   if (!MovDelay[x][y])          // next animation frame
9617     MovDelay[x][y] = 3 * delay;
9618
9619   if (MovDelay[x][y])           // wait some time before next frame
9620   {
9621     MovDelay[x][y]--;
9622
9623     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9624     {
9625       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9626       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9627
9628       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9629     }
9630
9631     if (!MovDelay[x][y])
9632     {
9633       if (MovDir[x][y] == MV_LEFT)
9634       {
9635         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9636           TEST_DrawLevelField(x - 1, y);
9637       }
9638       else if (MovDir[x][y] == MV_RIGHT)
9639       {
9640         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9641           TEST_DrawLevelField(x + 1, y);
9642       }
9643       else if (MovDir[x][y] == MV_UP)
9644       {
9645         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9646           TEST_DrawLevelField(x, y - 1);
9647       }
9648       else
9649       {
9650         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9651           TEST_DrawLevelField(x, y + 1);
9652       }
9653
9654       Tile[x][y] = Store[x][y];
9655       Store[x][y] = 0;
9656       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9657       TEST_DrawLevelField(x, y);
9658     }
9659   }
9660 }
9661
9662 static void MauerAbleger(int ax, int ay)
9663 {
9664   int element = Tile[ax][ay];
9665   int graphic = el2img(element);
9666   boolean oben_frei = FALSE, unten_frei = FALSE;
9667   boolean links_frei = FALSE, rechts_frei = FALSE;
9668   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9669   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9670   boolean new_wall = FALSE;
9671
9672   if (IS_ANIMATED(graphic))
9673     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9674
9675   if (!MovDelay[ax][ay])        // start building new wall
9676     MovDelay[ax][ay] = 6;
9677
9678   if (MovDelay[ax][ay])         // wait some time before building new wall
9679   {
9680     MovDelay[ax][ay]--;
9681     if (MovDelay[ax][ay])
9682       return;
9683   }
9684
9685   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9686     oben_frei = TRUE;
9687   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9688     unten_frei = TRUE;
9689   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9690     links_frei = TRUE;
9691   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9692     rechts_frei = TRUE;
9693
9694   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9695       element == EL_EXPANDABLE_WALL_ANY)
9696   {
9697     if (oben_frei)
9698     {
9699       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9700       Store[ax][ay-1] = element;
9701       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9702       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9703         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9704                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9705       new_wall = TRUE;
9706     }
9707     if (unten_frei)
9708     {
9709       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9710       Store[ax][ay+1] = element;
9711       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9712       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9713         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9714                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9715       new_wall = TRUE;
9716     }
9717   }
9718
9719   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9720       element == EL_EXPANDABLE_WALL_ANY ||
9721       element == EL_EXPANDABLE_WALL ||
9722       element == EL_BD_EXPANDABLE_WALL)
9723   {
9724     if (links_frei)
9725     {
9726       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9727       Store[ax-1][ay] = element;
9728       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9729       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9730         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9731                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9732       new_wall = TRUE;
9733     }
9734
9735     if (rechts_frei)
9736     {
9737       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9738       Store[ax+1][ay] = element;
9739       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9740       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9741         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9742                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9743       new_wall = TRUE;
9744     }
9745   }
9746
9747   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9748     TEST_DrawLevelField(ax, ay);
9749
9750   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9751     oben_massiv = TRUE;
9752   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9753     unten_massiv = TRUE;
9754   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9755     links_massiv = TRUE;
9756   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9757     rechts_massiv = TRUE;
9758
9759   if (((oben_massiv && unten_massiv) ||
9760        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9761        element == EL_EXPANDABLE_WALL) &&
9762       ((links_massiv && rechts_massiv) ||
9763        element == EL_EXPANDABLE_WALL_VERTICAL))
9764     Tile[ax][ay] = EL_WALL;
9765
9766   if (new_wall)
9767     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9768 }
9769
9770 static void MauerAblegerStahl(int ax, int ay)
9771 {
9772   int element = Tile[ax][ay];
9773   int graphic = el2img(element);
9774   boolean oben_frei = FALSE, unten_frei = FALSE;
9775   boolean links_frei = FALSE, rechts_frei = FALSE;
9776   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9777   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9778   boolean new_wall = FALSE;
9779
9780   if (IS_ANIMATED(graphic))
9781     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9782
9783   if (!MovDelay[ax][ay])        // start building new wall
9784     MovDelay[ax][ay] = 6;
9785
9786   if (MovDelay[ax][ay])         // wait some time before building new wall
9787   {
9788     MovDelay[ax][ay]--;
9789     if (MovDelay[ax][ay])
9790       return;
9791   }
9792
9793   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9794     oben_frei = TRUE;
9795   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9796     unten_frei = TRUE;
9797   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9798     links_frei = TRUE;
9799   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9800     rechts_frei = TRUE;
9801
9802   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9803       element == EL_EXPANDABLE_STEELWALL_ANY)
9804   {
9805     if (oben_frei)
9806     {
9807       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9808       Store[ax][ay-1] = element;
9809       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9810       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9811         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9812                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9813       new_wall = TRUE;
9814     }
9815     if (unten_frei)
9816     {
9817       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9818       Store[ax][ay+1] = element;
9819       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9820       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9821         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9822                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9823       new_wall = TRUE;
9824     }
9825   }
9826
9827   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9828       element == EL_EXPANDABLE_STEELWALL_ANY)
9829   {
9830     if (links_frei)
9831     {
9832       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9833       Store[ax-1][ay] = element;
9834       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9835       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9836         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9837                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9838       new_wall = TRUE;
9839     }
9840
9841     if (rechts_frei)
9842     {
9843       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9844       Store[ax+1][ay] = element;
9845       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9846       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9847         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9848                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9849       new_wall = TRUE;
9850     }
9851   }
9852
9853   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9854     oben_massiv = TRUE;
9855   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9856     unten_massiv = TRUE;
9857   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9858     links_massiv = TRUE;
9859   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9860     rechts_massiv = TRUE;
9861
9862   if (((oben_massiv && unten_massiv) ||
9863        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9864       ((links_massiv && rechts_massiv) ||
9865        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9866     Tile[ax][ay] = EL_STEELWALL;
9867
9868   if (new_wall)
9869     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9870 }
9871
9872 static void CheckForDragon(int x, int y)
9873 {
9874   int i, j;
9875   boolean dragon_found = FALSE;
9876   static int xy[4][2] =
9877   {
9878     { 0, -1 },
9879     { -1, 0 },
9880     { +1, 0 },
9881     { 0, +1 }
9882   };
9883
9884   for (i = 0; i < NUM_DIRECTIONS; i++)
9885   {
9886     for (j = 0; j < 4; j++)
9887     {
9888       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9889
9890       if (IN_LEV_FIELD(xx, yy) &&
9891           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9892       {
9893         if (Tile[xx][yy] == EL_DRAGON)
9894           dragon_found = TRUE;
9895       }
9896       else
9897         break;
9898     }
9899   }
9900
9901   if (!dragon_found)
9902   {
9903     for (i = 0; i < NUM_DIRECTIONS; i++)
9904     {
9905       for (j = 0; j < 3; j++)
9906       {
9907         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9908   
9909         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9910         {
9911           Tile[xx][yy] = EL_EMPTY;
9912           TEST_DrawLevelField(xx, yy);
9913         }
9914         else
9915           break;
9916       }
9917     }
9918   }
9919 }
9920
9921 static void InitBuggyBase(int x, int y)
9922 {
9923   int element = Tile[x][y];
9924   int activating_delay = FRAMES_PER_SECOND / 4;
9925
9926   ChangeDelay[x][y] =
9927     (element == EL_SP_BUGGY_BASE ?
9928      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9929      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9930      activating_delay :
9931      element == EL_SP_BUGGY_BASE_ACTIVE ?
9932      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9933 }
9934
9935 static void WarnBuggyBase(int x, int y)
9936 {
9937   int i;
9938   static int xy[4][2] =
9939   {
9940     { 0, -1 },
9941     { -1, 0 },
9942     { +1, 0 },
9943     { 0, +1 }
9944   };
9945
9946   for (i = 0; i < NUM_DIRECTIONS; i++)
9947   {
9948     int xx = x + xy[i][0];
9949     int yy = y + xy[i][1];
9950
9951     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9952     {
9953       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9954
9955       break;
9956     }
9957   }
9958 }
9959
9960 static void InitTrap(int x, int y)
9961 {
9962   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9963 }
9964
9965 static void ActivateTrap(int x, int y)
9966 {
9967   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9968 }
9969
9970 static void ChangeActiveTrap(int x, int y)
9971 {
9972   int graphic = IMG_TRAP_ACTIVE;
9973
9974   // if new animation frame was drawn, correct crumbled sand border
9975   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9976     TEST_DrawLevelFieldCrumbled(x, y);
9977 }
9978
9979 static int getSpecialActionElement(int element, int number, int base_element)
9980 {
9981   return (element != EL_EMPTY ? element :
9982           number != -1 ? base_element + number - 1 :
9983           EL_EMPTY);
9984 }
9985
9986 static int getModifiedActionNumber(int value_old, int operator, int operand,
9987                                    int value_min, int value_max)
9988 {
9989   int value_new = (operator == CA_MODE_SET      ? operand :
9990                    operator == CA_MODE_ADD      ? value_old + operand :
9991                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9992                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9993                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9994                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9995                    value_old);
9996
9997   return (value_new < value_min ? value_min :
9998           value_new > value_max ? value_max :
9999           value_new);
10000 }
10001
10002 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10003 {
10004   struct ElementInfo *ei = &element_info[element];
10005   struct ElementChangeInfo *change = &ei->change_page[page];
10006   int target_element = change->target_element;
10007   int action_type = change->action_type;
10008   int action_mode = change->action_mode;
10009   int action_arg = change->action_arg;
10010   int action_element = change->action_element;
10011   int i;
10012
10013   if (!change->has_action)
10014     return;
10015
10016   // ---------- determine action paramater values -----------------------------
10017
10018   int level_time_value =
10019     (level.time > 0 ? TimeLeft :
10020      TimePlayed);
10021
10022   int action_arg_element_raw =
10023     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10024      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10025      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10026      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10027      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10028      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10029      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10030      EL_EMPTY);
10031   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10032
10033   int action_arg_direction =
10034     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10035      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10036      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10037      change->actual_trigger_side :
10038      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10039      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10040      MV_NONE);
10041
10042   int action_arg_number_min =
10043     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10044      CA_ARG_MIN);
10045
10046   int action_arg_number_max =
10047     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10048      action_type == CA_SET_LEVEL_GEMS ? 999 :
10049      action_type == CA_SET_LEVEL_TIME ? 9999 :
10050      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10051      action_type == CA_SET_CE_VALUE ? 9999 :
10052      action_type == CA_SET_CE_SCORE ? 9999 :
10053      CA_ARG_MAX);
10054
10055   int action_arg_number_reset =
10056     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10057      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10058      action_type == CA_SET_LEVEL_TIME ? level.time :
10059      action_type == CA_SET_LEVEL_SCORE ? 0 :
10060      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10061      action_type == CA_SET_CE_SCORE ? 0 :
10062      0);
10063
10064   int action_arg_number =
10065     (action_arg <= CA_ARG_MAX ? action_arg :
10066      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10067      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10068      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10069      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10070      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10071      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10072      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10073      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10074      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10075      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10076      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10077      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10078      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10079      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10080      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10081      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10082      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10083      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10084      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10085      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10086      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10087      -1);
10088
10089   int action_arg_number_old =
10090     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10091      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10092      action_type == CA_SET_LEVEL_SCORE ? game.score :
10093      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10094      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10095      0);
10096
10097   int action_arg_number_new =
10098     getModifiedActionNumber(action_arg_number_old,
10099                             action_mode, action_arg_number,
10100                             action_arg_number_min, action_arg_number_max);
10101
10102   int trigger_player_bits =
10103     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10104      change->actual_trigger_player_bits : change->trigger_player);
10105
10106   int action_arg_player_bits =
10107     (action_arg >= CA_ARG_PLAYER_1 &&
10108      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10109      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10110      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10111      PLAYER_BITS_ANY);
10112
10113   // ---------- execute action  -----------------------------------------------
10114
10115   switch (action_type)
10116   {
10117     case CA_NO_ACTION:
10118     {
10119       return;
10120     }
10121
10122     // ---------- level actions  ----------------------------------------------
10123
10124     case CA_RESTART_LEVEL:
10125     {
10126       game.restart_level = TRUE;
10127
10128       break;
10129     }
10130
10131     case CA_SHOW_ENVELOPE:
10132     {
10133       int element = getSpecialActionElement(action_arg_element,
10134                                             action_arg_number, EL_ENVELOPE_1);
10135
10136       if (IS_ENVELOPE(element))
10137         local_player->show_envelope = element;
10138
10139       break;
10140     }
10141
10142     case CA_SET_LEVEL_TIME:
10143     {
10144       if (level.time > 0)       // only modify limited time value
10145       {
10146         TimeLeft = action_arg_number_new;
10147
10148         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10149
10150         DisplayGameControlValues();
10151
10152         if (!TimeLeft && setup.time_limit)
10153           for (i = 0; i < MAX_PLAYERS; i++)
10154             KillPlayer(&stored_player[i]);
10155       }
10156
10157       break;
10158     }
10159
10160     case CA_SET_LEVEL_SCORE:
10161     {
10162       game.score = action_arg_number_new;
10163
10164       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10165
10166       DisplayGameControlValues();
10167
10168       break;
10169     }
10170
10171     case CA_SET_LEVEL_GEMS:
10172     {
10173       game.gems_still_needed = action_arg_number_new;
10174
10175       game.snapshot.collected_item = TRUE;
10176
10177       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10178
10179       DisplayGameControlValues();
10180
10181       break;
10182     }
10183
10184     case CA_SET_LEVEL_WIND:
10185     {
10186       game.wind_direction = action_arg_direction;
10187
10188       break;
10189     }
10190
10191     case CA_SET_LEVEL_RANDOM_SEED:
10192     {
10193       // ensure that setting a new random seed while playing is predictable
10194       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10195
10196       break;
10197     }
10198
10199     // ---------- player actions  ---------------------------------------------
10200
10201     case CA_MOVE_PLAYER:
10202     case CA_MOVE_PLAYER_NEW:
10203     {
10204       // automatically move to the next field in specified direction
10205       for (i = 0; i < MAX_PLAYERS; i++)
10206         if (trigger_player_bits & (1 << i))
10207           if (action_type == CA_MOVE_PLAYER ||
10208               stored_player[i].MovPos == 0)
10209             stored_player[i].programmed_action = action_arg_direction;
10210
10211       break;
10212     }
10213
10214     case CA_EXIT_PLAYER:
10215     {
10216       for (i = 0; i < MAX_PLAYERS; i++)
10217         if (action_arg_player_bits & (1 << i))
10218           ExitPlayer(&stored_player[i]);
10219
10220       if (game.players_still_needed == 0)
10221         LevelSolved();
10222
10223       break;
10224     }
10225
10226     case CA_KILL_PLAYER:
10227     {
10228       for (i = 0; i < MAX_PLAYERS; i++)
10229         if (action_arg_player_bits & (1 << i))
10230           KillPlayer(&stored_player[i]);
10231
10232       break;
10233     }
10234
10235     case CA_SET_PLAYER_KEYS:
10236     {
10237       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10238       int element = getSpecialActionElement(action_arg_element,
10239                                             action_arg_number, EL_KEY_1);
10240
10241       if (IS_KEY(element))
10242       {
10243         for (i = 0; i < MAX_PLAYERS; i++)
10244         {
10245           if (trigger_player_bits & (1 << i))
10246           {
10247             stored_player[i].key[KEY_NR(element)] = key_state;
10248
10249             DrawGameDoorValues();
10250           }
10251         }
10252       }
10253
10254       break;
10255     }
10256
10257     case CA_SET_PLAYER_SPEED:
10258     {
10259       for (i = 0; i < MAX_PLAYERS; i++)
10260       {
10261         if (trigger_player_bits & (1 << i))
10262         {
10263           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10264
10265           if (action_arg == CA_ARG_SPEED_FASTER &&
10266               stored_player[i].cannot_move)
10267           {
10268             action_arg_number = STEPSIZE_VERY_SLOW;
10269           }
10270           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10271                    action_arg == CA_ARG_SPEED_FASTER)
10272           {
10273             action_arg_number = 2;
10274             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10275                            CA_MODE_MULTIPLY);
10276           }
10277           else if (action_arg == CA_ARG_NUMBER_RESET)
10278           {
10279             action_arg_number = level.initial_player_stepsize[i];
10280           }
10281
10282           move_stepsize =
10283             getModifiedActionNumber(move_stepsize,
10284                                     action_mode,
10285                                     action_arg_number,
10286                                     action_arg_number_min,
10287                                     action_arg_number_max);
10288
10289           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10290         }
10291       }
10292
10293       break;
10294     }
10295
10296     case CA_SET_PLAYER_SHIELD:
10297     {
10298       for (i = 0; i < MAX_PLAYERS; i++)
10299       {
10300         if (trigger_player_bits & (1 << i))
10301         {
10302           if (action_arg == CA_ARG_SHIELD_OFF)
10303           {
10304             stored_player[i].shield_normal_time_left = 0;
10305             stored_player[i].shield_deadly_time_left = 0;
10306           }
10307           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10308           {
10309             stored_player[i].shield_normal_time_left = 999999;
10310           }
10311           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10312           {
10313             stored_player[i].shield_normal_time_left = 999999;
10314             stored_player[i].shield_deadly_time_left = 999999;
10315           }
10316         }
10317       }
10318
10319       break;
10320     }
10321
10322     case CA_SET_PLAYER_GRAVITY:
10323     {
10324       for (i = 0; i < MAX_PLAYERS; i++)
10325       {
10326         if (trigger_player_bits & (1 << i))
10327         {
10328           stored_player[i].gravity =
10329             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10330              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10331              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10332              stored_player[i].gravity);
10333         }
10334       }
10335
10336       break;
10337     }
10338
10339     case CA_SET_PLAYER_ARTWORK:
10340     {
10341       for (i = 0; i < MAX_PLAYERS; i++)
10342       {
10343         if (trigger_player_bits & (1 << i))
10344         {
10345           int artwork_element = action_arg_element;
10346
10347           if (action_arg == CA_ARG_ELEMENT_RESET)
10348             artwork_element =
10349               (level.use_artwork_element[i] ? level.artwork_element[i] :
10350                stored_player[i].element_nr);
10351
10352           if (stored_player[i].artwork_element != artwork_element)
10353             stored_player[i].Frame = 0;
10354
10355           stored_player[i].artwork_element = artwork_element;
10356
10357           SetPlayerWaiting(&stored_player[i], FALSE);
10358
10359           // set number of special actions for bored and sleeping animation
10360           stored_player[i].num_special_action_bored =
10361             get_num_special_action(artwork_element,
10362                                    ACTION_BORING_1, ACTION_BORING_LAST);
10363           stored_player[i].num_special_action_sleeping =
10364             get_num_special_action(artwork_element,
10365                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10366         }
10367       }
10368
10369       break;
10370     }
10371
10372     case CA_SET_PLAYER_INVENTORY:
10373     {
10374       for (i = 0; i < MAX_PLAYERS; i++)
10375       {
10376         struct PlayerInfo *player = &stored_player[i];
10377         int j, k;
10378
10379         if (trigger_player_bits & (1 << i))
10380         {
10381           int inventory_element = action_arg_element;
10382
10383           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10384               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10385               action_arg == CA_ARG_ELEMENT_ACTION)
10386           {
10387             int element = inventory_element;
10388             int collect_count = element_info[element].collect_count_initial;
10389
10390             if (!IS_CUSTOM_ELEMENT(element))
10391               collect_count = 1;
10392
10393             if (collect_count == 0)
10394               player->inventory_infinite_element = element;
10395             else
10396               for (k = 0; k < collect_count; k++)
10397                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10398                   player->inventory_element[player->inventory_size++] =
10399                     element;
10400           }
10401           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10402                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10403                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10404           {
10405             if (player->inventory_infinite_element != EL_UNDEFINED &&
10406                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10407                                      action_arg_element_raw))
10408               player->inventory_infinite_element = EL_UNDEFINED;
10409
10410             for (k = 0, j = 0; j < player->inventory_size; j++)
10411             {
10412               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10413                                         action_arg_element_raw))
10414                 player->inventory_element[k++] = player->inventory_element[j];
10415             }
10416
10417             player->inventory_size = k;
10418           }
10419           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10420           {
10421             if (player->inventory_size > 0)
10422             {
10423               for (j = 0; j < player->inventory_size - 1; j++)
10424                 player->inventory_element[j] = player->inventory_element[j + 1];
10425
10426               player->inventory_size--;
10427             }
10428           }
10429           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10430           {
10431             if (player->inventory_size > 0)
10432               player->inventory_size--;
10433           }
10434           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10435           {
10436             player->inventory_infinite_element = EL_UNDEFINED;
10437             player->inventory_size = 0;
10438           }
10439           else if (action_arg == CA_ARG_INVENTORY_RESET)
10440           {
10441             player->inventory_infinite_element = EL_UNDEFINED;
10442             player->inventory_size = 0;
10443
10444             if (level.use_initial_inventory[i])
10445             {
10446               for (j = 0; j < level.initial_inventory_size[i]; j++)
10447               {
10448                 int element = level.initial_inventory_content[i][j];
10449                 int collect_count = element_info[element].collect_count_initial;
10450
10451                 if (!IS_CUSTOM_ELEMENT(element))
10452                   collect_count = 1;
10453
10454                 if (collect_count == 0)
10455                   player->inventory_infinite_element = element;
10456                 else
10457                   for (k = 0; k < collect_count; k++)
10458                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10459                       player->inventory_element[player->inventory_size++] =
10460                         element;
10461               }
10462             }
10463           }
10464         }
10465       }
10466
10467       break;
10468     }
10469
10470     // ---------- CE actions  -------------------------------------------------
10471
10472     case CA_SET_CE_VALUE:
10473     {
10474       int last_ce_value = CustomValue[x][y];
10475
10476       CustomValue[x][y] = action_arg_number_new;
10477
10478       if (CustomValue[x][y] != last_ce_value)
10479       {
10480         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10481         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10482
10483         if (CustomValue[x][y] == 0)
10484         {
10485           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10486           ChangeCount[x][y] = 0;        // allow at least one more change
10487
10488           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10489           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10490         }
10491       }
10492
10493       break;
10494     }
10495
10496     case CA_SET_CE_SCORE:
10497     {
10498       int last_ce_score = ei->collect_score;
10499
10500       ei->collect_score = action_arg_number_new;
10501
10502       if (ei->collect_score != last_ce_score)
10503       {
10504         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10505         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10506
10507         if (ei->collect_score == 0)
10508         {
10509           int xx, yy;
10510
10511           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10512           ChangeCount[x][y] = 0;        // allow at least one more change
10513
10514           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10515           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10516
10517           /*
10518             This is a very special case that seems to be a mixture between
10519             CheckElementChange() and CheckTriggeredElementChange(): while
10520             the first one only affects single elements that are triggered
10521             directly, the second one affects multiple elements in the playfield
10522             that are triggered indirectly by another element. This is a third
10523             case: Changing the CE score always affects multiple identical CEs,
10524             so every affected CE must be checked, not only the single CE for
10525             which the CE score was changed in the first place (as every instance
10526             of that CE shares the same CE score, and therefore also can change)!
10527           */
10528           SCAN_PLAYFIELD(xx, yy)
10529           {
10530             if (Tile[xx][yy] == element)
10531               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10532                                  CE_SCORE_GETS_ZERO);
10533           }
10534         }
10535       }
10536
10537       break;
10538     }
10539
10540     case CA_SET_CE_ARTWORK:
10541     {
10542       int artwork_element = action_arg_element;
10543       boolean reset_frame = FALSE;
10544       int xx, yy;
10545
10546       if (action_arg == CA_ARG_ELEMENT_RESET)
10547         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10548                            element);
10549
10550       if (ei->gfx_element != artwork_element)
10551         reset_frame = TRUE;
10552
10553       ei->gfx_element = artwork_element;
10554
10555       SCAN_PLAYFIELD(xx, yy)
10556       {
10557         if (Tile[xx][yy] == element)
10558         {
10559           if (reset_frame)
10560           {
10561             ResetGfxAnimation(xx, yy);
10562             ResetRandomAnimationValue(xx, yy);
10563           }
10564
10565           TEST_DrawLevelField(xx, yy);
10566         }
10567       }
10568
10569       break;
10570     }
10571
10572     // ---------- engine actions  ---------------------------------------------
10573
10574     case CA_SET_ENGINE_SCAN_MODE:
10575     {
10576       InitPlayfieldScanMode(action_arg);
10577
10578       break;
10579     }
10580
10581     default:
10582       break;
10583   }
10584 }
10585
10586 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10587 {
10588   int old_element = Tile[x][y];
10589   int new_element = GetElementFromGroupElement(element);
10590   int previous_move_direction = MovDir[x][y];
10591   int last_ce_value = CustomValue[x][y];
10592   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10593   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10594   boolean add_player_onto_element = (new_element_is_player &&
10595                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10596                                      IS_WALKABLE(old_element));
10597
10598   if (!add_player_onto_element)
10599   {
10600     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10601       RemoveMovingField(x, y);
10602     else
10603       RemoveField(x, y);
10604
10605     Tile[x][y] = new_element;
10606
10607     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10608       MovDir[x][y] = previous_move_direction;
10609
10610     if (element_info[new_element].use_last_ce_value)
10611       CustomValue[x][y] = last_ce_value;
10612
10613     InitField_WithBug1(x, y, FALSE);
10614
10615     new_element = Tile[x][y];   // element may have changed
10616
10617     ResetGfxAnimation(x, y);
10618     ResetRandomAnimationValue(x, y);
10619
10620     TEST_DrawLevelField(x, y);
10621
10622     if (GFX_CRUMBLED(new_element))
10623       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10624   }
10625
10626   // check if element under the player changes from accessible to unaccessible
10627   // (needed for special case of dropping element which then changes)
10628   // (must be checked after creating new element for walkable group elements)
10629   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10630       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10631   {
10632     Bang(x, y);
10633
10634     return;
10635   }
10636
10637   // "ChangeCount" not set yet to allow "entered by player" change one time
10638   if (new_element_is_player)
10639     RelocatePlayer(x, y, new_element);
10640
10641   if (is_change)
10642     ChangeCount[x][y]++;        // count number of changes in the same frame
10643
10644   TestIfBadThingTouchesPlayer(x, y);
10645   TestIfPlayerTouchesCustomElement(x, y);
10646   TestIfElementTouchesCustomElement(x, y);
10647 }
10648
10649 static void CreateField(int x, int y, int element)
10650 {
10651   CreateFieldExt(x, y, element, FALSE);
10652 }
10653
10654 static void CreateElementFromChange(int x, int y, int element)
10655 {
10656   element = GET_VALID_RUNTIME_ELEMENT(element);
10657
10658   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10659   {
10660     int old_element = Tile[x][y];
10661
10662     // prevent changed element from moving in same engine frame
10663     // unless both old and new element can either fall or move
10664     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10665         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10666       Stop[x][y] = TRUE;
10667   }
10668
10669   CreateFieldExt(x, y, element, TRUE);
10670 }
10671
10672 static boolean ChangeElement(int x, int y, int element, int page)
10673 {
10674   struct ElementInfo *ei = &element_info[element];
10675   struct ElementChangeInfo *change = &ei->change_page[page];
10676   int ce_value = CustomValue[x][y];
10677   int ce_score = ei->collect_score;
10678   int target_element;
10679   int old_element = Tile[x][y];
10680
10681   // always use default change event to prevent running into a loop
10682   if (ChangeEvent[x][y] == -1)
10683     ChangeEvent[x][y] = CE_DELAY;
10684
10685   if (ChangeEvent[x][y] == CE_DELAY)
10686   {
10687     // reset actual trigger element, trigger player and action element
10688     change->actual_trigger_element = EL_EMPTY;
10689     change->actual_trigger_player = EL_EMPTY;
10690     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10691     change->actual_trigger_side = CH_SIDE_NONE;
10692     change->actual_trigger_ce_value = 0;
10693     change->actual_trigger_ce_score = 0;
10694   }
10695
10696   // do not change elements more than a specified maximum number of changes
10697   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10698     return FALSE;
10699
10700   ChangeCount[x][y]++;          // count number of changes in the same frame
10701
10702   if (change->explode)
10703   {
10704     Bang(x, y);
10705
10706     return TRUE;
10707   }
10708
10709   if (change->use_target_content)
10710   {
10711     boolean complete_replace = TRUE;
10712     boolean can_replace[3][3];
10713     int xx, yy;
10714
10715     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10716     {
10717       boolean is_empty;
10718       boolean is_walkable;
10719       boolean is_diggable;
10720       boolean is_collectible;
10721       boolean is_removable;
10722       boolean is_destructible;
10723       int ex = x + xx - 1;
10724       int ey = y + yy - 1;
10725       int content_element = change->target_content.e[xx][yy];
10726       int e;
10727
10728       can_replace[xx][yy] = TRUE;
10729
10730       if (ex == x && ey == y)   // do not check changing element itself
10731         continue;
10732
10733       if (content_element == EL_EMPTY_SPACE)
10734       {
10735         can_replace[xx][yy] = FALSE;    // do not replace border with space
10736
10737         continue;
10738       }
10739
10740       if (!IN_LEV_FIELD(ex, ey))
10741       {
10742         can_replace[xx][yy] = FALSE;
10743         complete_replace = FALSE;
10744
10745         continue;
10746       }
10747
10748       e = Tile[ex][ey];
10749
10750       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10751         e = MovingOrBlocked2Element(ex, ey);
10752
10753       is_empty = (IS_FREE(ex, ey) ||
10754                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10755
10756       is_walkable     = (is_empty || IS_WALKABLE(e));
10757       is_diggable     = (is_empty || IS_DIGGABLE(e));
10758       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10759       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10760       is_removable    = (is_diggable || is_collectible);
10761
10762       can_replace[xx][yy] =
10763         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10764           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10765           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10766           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10767           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10768           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10769          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10770
10771       if (!can_replace[xx][yy])
10772         complete_replace = FALSE;
10773     }
10774
10775     if (!change->only_if_complete || complete_replace)
10776     {
10777       boolean something_has_changed = FALSE;
10778
10779       if (change->only_if_complete && change->use_random_replace &&
10780           RND(100) < change->random_percentage)
10781         return FALSE;
10782
10783       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10784       {
10785         int ex = x + xx - 1;
10786         int ey = y + yy - 1;
10787         int content_element;
10788
10789         if (can_replace[xx][yy] && (!change->use_random_replace ||
10790                                     RND(100) < change->random_percentage))
10791         {
10792           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10793             RemoveMovingField(ex, ey);
10794
10795           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10796
10797           content_element = change->target_content.e[xx][yy];
10798           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10799                                               ce_value, ce_score);
10800
10801           CreateElementFromChange(ex, ey, target_element);
10802
10803           something_has_changed = TRUE;
10804
10805           // for symmetry reasons, freeze newly created border elements
10806           if (ex != x || ey != y)
10807             Stop[ex][ey] = TRUE;        // no more moving in this frame
10808         }
10809       }
10810
10811       if (something_has_changed)
10812       {
10813         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10814         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10815       }
10816     }
10817   }
10818   else
10819   {
10820     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10821                                         ce_value, ce_score);
10822
10823     if (element == EL_DIAGONAL_GROWING ||
10824         element == EL_DIAGONAL_SHRINKING)
10825     {
10826       target_element = Store[x][y];
10827
10828       Store[x][y] = EL_EMPTY;
10829     }
10830
10831     // special case: element changes to player (and may be kept if walkable)
10832     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10833       CreateElementFromChange(x, y, EL_EMPTY);
10834
10835     CreateElementFromChange(x, y, target_element);
10836
10837     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10838     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10839   }
10840
10841   // this uses direct change before indirect change
10842   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10843
10844   return TRUE;
10845 }
10846
10847 static void HandleElementChange(int x, int y, int page)
10848 {
10849   int element = MovingOrBlocked2Element(x, y);
10850   struct ElementInfo *ei = &element_info[element];
10851   struct ElementChangeInfo *change = &ei->change_page[page];
10852   boolean handle_action_before_change = FALSE;
10853
10854 #ifdef DEBUG
10855   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10856       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10857   {
10858     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10859           x, y, element, element_info[element].token_name);
10860     Debug("game:playing:HandleElementChange", "This should never happen!");
10861   }
10862 #endif
10863
10864   // this can happen with classic bombs on walkable, changing elements
10865   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10866   {
10867     return;
10868   }
10869
10870   if (ChangeDelay[x][y] == 0)           // initialize element change
10871   {
10872     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10873
10874     if (change->can_change)
10875     {
10876       // !!! not clear why graphic animation should be reset at all here !!!
10877       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10878       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10879
10880       /*
10881         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10882
10883         When using an animation frame delay of 1 (this only happens with
10884         "sp_zonk.moving.left/right" in the classic graphics), the default
10885         (non-moving) animation shows wrong animation frames (while the
10886         moving animation, like "sp_zonk.moving.left/right", is correct,
10887         so this graphical bug never shows up with the classic graphics).
10888         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10889         be drawn instead of the correct frames 0,1,2,3. This is caused by
10890         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10891         an element change: First when the change delay ("ChangeDelay[][]")
10892         counter has reached zero after decrementing, then a second time in
10893         the next frame (after "GfxFrame[][]" was already incremented) when
10894         "ChangeDelay[][]" is reset to the initial delay value again.
10895
10896         This causes frame 0 to be drawn twice, while the last frame won't
10897         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10898
10899         As some animations may already be cleverly designed around this bug
10900         (at least the "Snake Bite" snake tail animation does this), it cannot
10901         simply be fixed here without breaking such existing animations.
10902         Unfortunately, it cannot easily be detected if a graphics set was
10903         designed "before" or "after" the bug was fixed. As a workaround,
10904         a new graphics set option "game.graphics_engine_version" was added
10905         to be able to specify the game's major release version for which the
10906         graphics set was designed, which can then be used to decide if the
10907         bugfix should be used (version 4 and above) or not (version 3 or
10908         below, or if no version was specified at all, as with old sets).
10909
10910         (The wrong/fixed animation frames can be tested with the test level set
10911         "test_gfxframe" and level "000", which contains a specially prepared
10912         custom element at level position (x/y) == (11/9) which uses the zonk
10913         animation mentioned above. Using "game.graphics_engine_version: 4"
10914         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10915         This can also be seen from the debug output for this test element.)
10916       */
10917
10918       // when a custom element is about to change (for example by change delay),
10919       // do not reset graphic animation when the custom element is moving
10920       if (game.graphics_engine_version < 4 &&
10921           !IS_MOVING(x, y))
10922       {
10923         ResetGfxAnimation(x, y);
10924         ResetRandomAnimationValue(x, y);
10925       }
10926
10927       if (change->pre_change_function)
10928         change->pre_change_function(x, y);
10929     }
10930   }
10931
10932   ChangeDelay[x][y]--;
10933
10934   if (ChangeDelay[x][y] != 0)           // continue element change
10935   {
10936     if (change->can_change)
10937     {
10938       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10939
10940       if (IS_ANIMATED(graphic))
10941         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10942
10943       if (change->change_function)
10944         change->change_function(x, y);
10945     }
10946   }
10947   else                                  // finish element change
10948   {
10949     if (ChangePage[x][y] != -1)         // remember page from delayed change
10950     {
10951       page = ChangePage[x][y];
10952       ChangePage[x][y] = -1;
10953
10954       change = &ei->change_page[page];
10955     }
10956
10957     if (IS_MOVING(x, y))                // never change a running system ;-)
10958     {
10959       ChangeDelay[x][y] = 1;            // try change after next move step
10960       ChangePage[x][y] = page;          // remember page to use for change
10961
10962       return;
10963     }
10964
10965     // special case: set new level random seed before changing element
10966     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10967       handle_action_before_change = TRUE;
10968
10969     if (change->has_action && handle_action_before_change)
10970       ExecuteCustomElementAction(x, y, element, page);
10971
10972     if (change->can_change)
10973     {
10974       if (ChangeElement(x, y, element, page))
10975       {
10976         if (change->post_change_function)
10977           change->post_change_function(x, y);
10978       }
10979     }
10980
10981     if (change->has_action && !handle_action_before_change)
10982       ExecuteCustomElementAction(x, y, element, page);
10983   }
10984 }
10985
10986 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10987                                               int trigger_element,
10988                                               int trigger_event,
10989                                               int trigger_player,
10990                                               int trigger_side,
10991                                               int trigger_page)
10992 {
10993   boolean change_done_any = FALSE;
10994   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10995   int i;
10996
10997   if (!(trigger_events[trigger_element][trigger_event]))
10998     return FALSE;
10999
11000   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11001
11002   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11003   {
11004     int element = EL_CUSTOM_START + i;
11005     boolean change_done = FALSE;
11006     int p;
11007
11008     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11009         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11010       continue;
11011
11012     for (p = 0; p < element_info[element].num_change_pages; p++)
11013     {
11014       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11015
11016       if (change->can_change_or_has_action &&
11017           change->has_event[trigger_event] &&
11018           change->trigger_side & trigger_side &&
11019           change->trigger_player & trigger_player &&
11020           change->trigger_page & trigger_page_bits &&
11021           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11022       {
11023         change->actual_trigger_element = trigger_element;
11024         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11025         change->actual_trigger_player_bits = trigger_player;
11026         change->actual_trigger_side = trigger_side;
11027         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11028         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11029
11030         if ((change->can_change && !change_done) || change->has_action)
11031         {
11032           int x, y;
11033
11034           SCAN_PLAYFIELD(x, y)
11035           {
11036             if (Tile[x][y] == element)
11037             {
11038               if (change->can_change && !change_done)
11039               {
11040                 // if element already changed in this frame, not only prevent
11041                 // another element change (checked in ChangeElement()), but
11042                 // also prevent additional element actions for this element
11043
11044                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11045                     !level.use_action_after_change_bug)
11046                   continue;
11047
11048                 ChangeDelay[x][y] = 1;
11049                 ChangeEvent[x][y] = trigger_event;
11050
11051                 HandleElementChange(x, y, p);
11052               }
11053               else if (change->has_action)
11054               {
11055                 // if element already changed in this frame, not only prevent
11056                 // another element change (checked in ChangeElement()), but
11057                 // also prevent additional element actions for this element
11058
11059                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11060                     !level.use_action_after_change_bug)
11061                   continue;
11062
11063                 ExecuteCustomElementAction(x, y, element, p);
11064                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11065               }
11066             }
11067           }
11068
11069           if (change->can_change)
11070           {
11071             change_done = TRUE;
11072             change_done_any = TRUE;
11073           }
11074         }
11075       }
11076     }
11077   }
11078
11079   RECURSION_LOOP_DETECTION_END();
11080
11081   return change_done_any;
11082 }
11083
11084 static boolean CheckElementChangeExt(int x, int y,
11085                                      int element,
11086                                      int trigger_element,
11087                                      int trigger_event,
11088                                      int trigger_player,
11089                                      int trigger_side)
11090 {
11091   boolean change_done = FALSE;
11092   int p;
11093
11094   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11095       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11096     return FALSE;
11097
11098   if (Tile[x][y] == EL_BLOCKED)
11099   {
11100     Blocked2Moving(x, y, &x, &y);
11101     element = Tile[x][y];
11102   }
11103
11104   // check if element has already changed or is about to change after moving
11105   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11106        Tile[x][y] != element) ||
11107
11108       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11109        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11110         ChangePage[x][y] != -1)))
11111     return FALSE;
11112
11113   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11114
11115   for (p = 0; p < element_info[element].num_change_pages; p++)
11116   {
11117     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11118
11119     /* check trigger element for all events where the element that is checked
11120        for changing interacts with a directly adjacent element -- this is
11121        different to element changes that affect other elements to change on the
11122        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11123     boolean check_trigger_element =
11124       (trigger_event == CE_TOUCHING_X ||
11125        trigger_event == CE_HITTING_X ||
11126        trigger_event == CE_HIT_BY_X ||
11127        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11128
11129     if (change->can_change_or_has_action &&
11130         change->has_event[trigger_event] &&
11131         change->trigger_side & trigger_side &&
11132         change->trigger_player & trigger_player &&
11133         (!check_trigger_element ||
11134          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11135     {
11136       change->actual_trigger_element = trigger_element;
11137       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11138       change->actual_trigger_player_bits = trigger_player;
11139       change->actual_trigger_side = trigger_side;
11140       change->actual_trigger_ce_value = CustomValue[x][y];
11141       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11142
11143       // special case: trigger element not at (x,y) position for some events
11144       if (check_trigger_element)
11145       {
11146         static struct
11147         {
11148           int dx, dy;
11149         } move_xy[] =
11150           {
11151             {  0,  0 },
11152             { -1,  0 },
11153             { +1,  0 },
11154             {  0,  0 },
11155             {  0, -1 },
11156             {  0,  0 }, { 0, 0 }, { 0, 0 },
11157             {  0, +1 }
11158           };
11159
11160         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11161         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11162
11163         change->actual_trigger_ce_value = CustomValue[xx][yy];
11164         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11165       }
11166
11167       if (change->can_change && !change_done)
11168       {
11169         ChangeDelay[x][y] = 1;
11170         ChangeEvent[x][y] = trigger_event;
11171
11172         HandleElementChange(x, y, p);
11173
11174         change_done = TRUE;
11175       }
11176       else if (change->has_action)
11177       {
11178         ExecuteCustomElementAction(x, y, element, p);
11179         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11180       }
11181     }
11182   }
11183
11184   RECURSION_LOOP_DETECTION_END();
11185
11186   return change_done;
11187 }
11188
11189 static void PlayPlayerSound(struct PlayerInfo *player)
11190 {
11191   int jx = player->jx, jy = player->jy;
11192   int sound_element = player->artwork_element;
11193   int last_action = player->last_action_waiting;
11194   int action = player->action_waiting;
11195
11196   if (player->is_waiting)
11197   {
11198     if (action != last_action)
11199       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11200     else
11201       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11202   }
11203   else
11204   {
11205     if (action != last_action)
11206       StopSound(element_info[sound_element].sound[last_action]);
11207
11208     if (last_action == ACTION_SLEEPING)
11209       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11210   }
11211 }
11212
11213 static void PlayAllPlayersSound(void)
11214 {
11215   int i;
11216
11217   for (i = 0; i < MAX_PLAYERS; i++)
11218     if (stored_player[i].active)
11219       PlayPlayerSound(&stored_player[i]);
11220 }
11221
11222 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11223 {
11224   boolean last_waiting = player->is_waiting;
11225   int move_dir = player->MovDir;
11226
11227   player->dir_waiting = move_dir;
11228   player->last_action_waiting = player->action_waiting;
11229
11230   if (is_waiting)
11231   {
11232     if (!last_waiting)          // not waiting -> waiting
11233     {
11234       player->is_waiting = TRUE;
11235
11236       player->frame_counter_bored =
11237         FrameCounter +
11238         game.player_boring_delay_fixed +
11239         GetSimpleRandom(game.player_boring_delay_random);
11240       player->frame_counter_sleeping =
11241         FrameCounter +
11242         game.player_sleeping_delay_fixed +
11243         GetSimpleRandom(game.player_sleeping_delay_random);
11244
11245       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11246     }
11247
11248     if (game.player_sleeping_delay_fixed +
11249         game.player_sleeping_delay_random > 0 &&
11250         player->anim_delay_counter == 0 &&
11251         player->post_delay_counter == 0 &&
11252         FrameCounter >= player->frame_counter_sleeping)
11253       player->is_sleeping = TRUE;
11254     else if (game.player_boring_delay_fixed +
11255              game.player_boring_delay_random > 0 &&
11256              FrameCounter >= player->frame_counter_bored)
11257       player->is_bored = TRUE;
11258
11259     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11260                               player->is_bored ? ACTION_BORING :
11261                               ACTION_WAITING);
11262
11263     if (player->is_sleeping && player->use_murphy)
11264     {
11265       // special case for sleeping Murphy when leaning against non-free tile
11266
11267       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11268           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11269            !IS_MOVING(player->jx - 1, player->jy)))
11270         move_dir = MV_LEFT;
11271       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11272                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11273                 !IS_MOVING(player->jx + 1, player->jy)))
11274         move_dir = MV_RIGHT;
11275       else
11276         player->is_sleeping = FALSE;
11277
11278       player->dir_waiting = move_dir;
11279     }
11280
11281     if (player->is_sleeping)
11282     {
11283       if (player->num_special_action_sleeping > 0)
11284       {
11285         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11286         {
11287           int last_special_action = player->special_action_sleeping;
11288           int num_special_action = player->num_special_action_sleeping;
11289           int special_action =
11290             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11291              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11292              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11293              last_special_action + 1 : ACTION_SLEEPING);
11294           int special_graphic =
11295             el_act_dir2img(player->artwork_element, special_action, move_dir);
11296
11297           player->anim_delay_counter =
11298             graphic_info[special_graphic].anim_delay_fixed +
11299             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11300           player->post_delay_counter =
11301             graphic_info[special_graphic].post_delay_fixed +
11302             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11303
11304           player->special_action_sleeping = special_action;
11305         }
11306
11307         if (player->anim_delay_counter > 0)
11308         {
11309           player->action_waiting = player->special_action_sleeping;
11310           player->anim_delay_counter--;
11311         }
11312         else if (player->post_delay_counter > 0)
11313         {
11314           player->post_delay_counter--;
11315         }
11316       }
11317     }
11318     else if (player->is_bored)
11319     {
11320       if (player->num_special_action_bored > 0)
11321       {
11322         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11323         {
11324           int special_action =
11325             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11326           int special_graphic =
11327             el_act_dir2img(player->artwork_element, special_action, move_dir);
11328
11329           player->anim_delay_counter =
11330             graphic_info[special_graphic].anim_delay_fixed +
11331             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11332           player->post_delay_counter =
11333             graphic_info[special_graphic].post_delay_fixed +
11334             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11335
11336           player->special_action_bored = special_action;
11337         }
11338
11339         if (player->anim_delay_counter > 0)
11340         {
11341           player->action_waiting = player->special_action_bored;
11342           player->anim_delay_counter--;
11343         }
11344         else if (player->post_delay_counter > 0)
11345         {
11346           player->post_delay_counter--;
11347         }
11348       }
11349     }
11350   }
11351   else if (last_waiting)        // waiting -> not waiting
11352   {
11353     player->is_waiting = FALSE;
11354     player->is_bored = FALSE;
11355     player->is_sleeping = FALSE;
11356
11357     player->frame_counter_bored = -1;
11358     player->frame_counter_sleeping = -1;
11359
11360     player->anim_delay_counter = 0;
11361     player->post_delay_counter = 0;
11362
11363     player->dir_waiting = player->MovDir;
11364     player->action_waiting = ACTION_DEFAULT;
11365
11366     player->special_action_bored = ACTION_DEFAULT;
11367     player->special_action_sleeping = ACTION_DEFAULT;
11368   }
11369 }
11370
11371 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11372 {
11373   if ((!player->is_moving  && player->was_moving) ||
11374       (player->MovPos == 0 && player->was_moving) ||
11375       (player->is_snapping && !player->was_snapping) ||
11376       (player->is_dropping && !player->was_dropping))
11377   {
11378     if (!CheckSaveEngineSnapshotToList())
11379       return;
11380
11381     player->was_moving = FALSE;
11382     player->was_snapping = TRUE;
11383     player->was_dropping = TRUE;
11384   }
11385   else
11386   {
11387     if (player->is_moving)
11388       player->was_moving = TRUE;
11389
11390     if (!player->is_snapping)
11391       player->was_snapping = FALSE;
11392
11393     if (!player->is_dropping)
11394       player->was_dropping = FALSE;
11395   }
11396
11397   static struct MouseActionInfo mouse_action_last = { 0 };
11398   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11399   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11400
11401   if (new_released)
11402     CheckSaveEngineSnapshotToList();
11403
11404   mouse_action_last = mouse_action;
11405 }
11406
11407 static void CheckSingleStepMode(struct PlayerInfo *player)
11408 {
11409   if (tape.single_step && tape.recording && !tape.pausing)
11410   {
11411     // as it is called "single step mode", just return to pause mode when the
11412     // player stopped moving after one tile (or never starts moving at all)
11413     // (reverse logic needed here in case single step mode used in team mode)
11414     if (player->is_moving ||
11415         player->is_pushing ||
11416         player->is_dropping_pressed ||
11417         player->effective_mouse_action.button)
11418       game.enter_single_step_mode = FALSE;
11419   }
11420
11421   CheckSaveEngineSnapshot(player);
11422 }
11423
11424 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11425 {
11426   int left      = player_action & JOY_LEFT;
11427   int right     = player_action & JOY_RIGHT;
11428   int up        = player_action & JOY_UP;
11429   int down      = player_action & JOY_DOWN;
11430   int button1   = player_action & JOY_BUTTON_1;
11431   int button2   = player_action & JOY_BUTTON_2;
11432   int dx        = (left ? -1 : right ? 1 : 0);
11433   int dy        = (up   ? -1 : down  ? 1 : 0);
11434
11435   if (!player->active || tape.pausing)
11436     return 0;
11437
11438   if (player_action)
11439   {
11440     if (button1)
11441       SnapField(player, dx, dy);
11442     else
11443     {
11444       if (button2)
11445         DropElement(player);
11446
11447       MovePlayer(player, dx, dy);
11448     }
11449
11450     CheckSingleStepMode(player);
11451
11452     SetPlayerWaiting(player, FALSE);
11453
11454     return player_action;
11455   }
11456   else
11457   {
11458     // no actions for this player (no input at player's configured device)
11459
11460     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11461     SnapField(player, 0, 0);
11462     CheckGravityMovementWhenNotMoving(player);
11463
11464     if (player->MovPos == 0)
11465       SetPlayerWaiting(player, TRUE);
11466
11467     if (player->MovPos == 0)    // needed for tape.playing
11468       player->is_moving = FALSE;
11469
11470     player->is_dropping = FALSE;
11471     player->is_dropping_pressed = FALSE;
11472     player->drop_pressed_delay = 0;
11473
11474     CheckSingleStepMode(player);
11475
11476     return 0;
11477   }
11478 }
11479
11480 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11481                                          byte *tape_action)
11482 {
11483   if (!tape.use_mouse_actions)
11484     return;
11485
11486   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11487   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11488   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11489 }
11490
11491 static void SetTapeActionFromMouseAction(byte *tape_action,
11492                                          struct MouseActionInfo *mouse_action)
11493 {
11494   if (!tape.use_mouse_actions)
11495     return;
11496
11497   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11498   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11499   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11500 }
11501
11502 static void CheckLevelSolved(void)
11503 {
11504   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11505   {
11506     if (game_em.level_solved &&
11507         !game_em.game_over)                             // game won
11508     {
11509       LevelSolved();
11510
11511       game_em.game_over = TRUE;
11512
11513       game.all_players_gone = TRUE;
11514     }
11515
11516     if (game_em.game_over)                              // game lost
11517       game.all_players_gone = TRUE;
11518   }
11519   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11520   {
11521     if (game_sp.level_solved &&
11522         !game_sp.game_over)                             // game won
11523     {
11524       LevelSolved();
11525
11526       game_sp.game_over = TRUE;
11527
11528       game.all_players_gone = TRUE;
11529     }
11530
11531     if (game_sp.game_over)                              // game lost
11532       game.all_players_gone = TRUE;
11533   }
11534   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11535   {
11536     if (game_mm.level_solved &&
11537         !game_mm.game_over)                             // game won
11538     {
11539       LevelSolved();
11540
11541       game_mm.game_over = TRUE;
11542
11543       game.all_players_gone = TRUE;
11544     }
11545
11546     if (game_mm.game_over)                              // game lost
11547       game.all_players_gone = TRUE;
11548   }
11549 }
11550
11551 static void CheckLevelTime(void)
11552 {
11553   int i;
11554
11555   if (TimeFrames >= FRAMES_PER_SECOND)
11556   {
11557     TimeFrames = 0;
11558     TapeTime++;
11559
11560     for (i = 0; i < MAX_PLAYERS; i++)
11561     {
11562       struct PlayerInfo *player = &stored_player[i];
11563
11564       if (SHIELD_ON(player))
11565       {
11566         player->shield_normal_time_left--;
11567
11568         if (player->shield_deadly_time_left > 0)
11569           player->shield_deadly_time_left--;
11570       }
11571     }
11572
11573     if (!game.LevelSolved && !level.use_step_counter)
11574     {
11575       TimePlayed++;
11576
11577       if (TimeLeft > 0)
11578       {
11579         TimeLeft--;
11580
11581         if (TimeLeft <= 10 && setup.time_limit)
11582           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11583
11584         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11585            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11586
11587         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11588
11589         if (!TimeLeft && setup.time_limit)
11590         {
11591           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11592             game_em.lev->killed_out_of_time = TRUE;
11593           else
11594             for (i = 0; i < MAX_PLAYERS; i++)
11595               KillPlayer(&stored_player[i]);
11596         }
11597       }
11598       else if (game.no_time_limit && !game.all_players_gone)
11599       {
11600         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11601       }
11602
11603       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11604     }
11605
11606     if (tape.recording || tape.playing)
11607       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11608   }
11609
11610   if (tape.recording || tape.playing)
11611     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11612
11613   UpdateAndDisplayGameControlValues();
11614 }
11615
11616 void AdvanceFrameAndPlayerCounters(int player_nr)
11617 {
11618   int i;
11619
11620   // advance frame counters (global frame counter and time frame counter)
11621   FrameCounter++;
11622   TimeFrames++;
11623
11624   // advance player counters (counters for move delay, move animation etc.)
11625   for (i = 0; i < MAX_PLAYERS; i++)
11626   {
11627     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11628     int move_delay_value = stored_player[i].move_delay_value;
11629     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11630
11631     if (!advance_player_counters)       // not all players may be affected
11632       continue;
11633
11634     if (move_frames == 0)       // less than one move per game frame
11635     {
11636       int stepsize = TILEX / move_delay_value;
11637       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11638       int count = (stored_player[i].is_moving ?
11639                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11640
11641       if (count % delay == 0)
11642         move_frames = 1;
11643     }
11644
11645     stored_player[i].Frame += move_frames;
11646
11647     if (stored_player[i].MovPos != 0)
11648       stored_player[i].StepFrame += move_frames;
11649
11650     if (stored_player[i].move_delay > 0)
11651       stored_player[i].move_delay--;
11652
11653     // due to bugs in previous versions, counter must count up, not down
11654     if (stored_player[i].push_delay != -1)
11655       stored_player[i].push_delay++;
11656
11657     if (stored_player[i].drop_delay > 0)
11658       stored_player[i].drop_delay--;
11659
11660     if (stored_player[i].is_dropping_pressed)
11661       stored_player[i].drop_pressed_delay++;
11662   }
11663 }
11664
11665 void StartGameActions(boolean init_network_game, boolean record_tape,
11666                       int random_seed)
11667 {
11668   unsigned int new_random_seed = InitRND(random_seed);
11669
11670   if (record_tape)
11671     TapeStartRecording(new_random_seed);
11672
11673   if (init_network_game)
11674   {
11675     SendToServer_LevelFile();
11676     SendToServer_StartPlaying();
11677
11678     return;
11679   }
11680
11681   InitGame();
11682 }
11683
11684 static void GameActionsExt(void)
11685 {
11686 #if 0
11687   static unsigned int game_frame_delay = 0;
11688 #endif
11689   unsigned int game_frame_delay_value;
11690   byte *recorded_player_action;
11691   byte summarized_player_action = 0;
11692   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11693   int i;
11694
11695   // detect endless loops, caused by custom element programming
11696   if (recursion_loop_detected && recursion_loop_depth == 0)
11697   {
11698     char *message = getStringCat3("Internal Error! Element ",
11699                                   EL_NAME(recursion_loop_element),
11700                                   " caused endless loop! Quit the game?");
11701
11702     Warn("element '%s' caused endless loop in game engine",
11703          EL_NAME(recursion_loop_element));
11704
11705     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11706
11707     recursion_loop_detected = FALSE;    // if game should be continued
11708
11709     free(message);
11710
11711     return;
11712   }
11713
11714   if (game.restart_level)
11715     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11716
11717   CheckLevelSolved();
11718
11719   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11720     GameWon();
11721
11722   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11723     TapeStop();
11724
11725   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11726     return;
11727
11728   game_frame_delay_value =
11729     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11730
11731   if (tape.playing && tape.warp_forward && !tape.pausing)
11732     game_frame_delay_value = 0;
11733
11734   SetVideoFrameDelay(game_frame_delay_value);
11735
11736   // (de)activate virtual buttons depending on current game status
11737   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11738   {
11739     if (game.all_players_gone)  // if no players there to be controlled anymore
11740       SetOverlayActive(FALSE);
11741     else if (!tape.playing)     // if game continues after tape stopped playing
11742       SetOverlayActive(TRUE);
11743   }
11744
11745 #if 0
11746 #if 0
11747   // ---------- main game synchronization point ----------
11748
11749   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11750
11751   Debug("game:playing:skip", "skip == %d", skip);
11752
11753 #else
11754   // ---------- main game synchronization point ----------
11755
11756   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11757 #endif
11758 #endif
11759
11760   if (network_playing && !network_player_action_received)
11761   {
11762     // try to get network player actions in time
11763
11764     // last chance to get network player actions without main loop delay
11765     HandleNetworking();
11766
11767     // game was quit by network peer
11768     if (game_status != GAME_MODE_PLAYING)
11769       return;
11770
11771     // check if network player actions still missing and game still running
11772     if (!network_player_action_received && !checkGameEnded())
11773       return;           // failed to get network player actions in time
11774
11775     // do not yet reset "network_player_action_received" (for tape.pausing)
11776   }
11777
11778   if (tape.pausing)
11779     return;
11780
11781   // at this point we know that we really continue executing the game
11782
11783   network_player_action_received = FALSE;
11784
11785   // when playing tape, read previously recorded player input from tape data
11786   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11787
11788   local_player->effective_mouse_action = local_player->mouse_action;
11789
11790   if (recorded_player_action != NULL)
11791     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11792                                  recorded_player_action);
11793
11794   // TapePlayAction() may return NULL when toggling to "pause before death"
11795   if (tape.pausing)
11796     return;
11797
11798   if (tape.set_centered_player)
11799   {
11800     game.centered_player_nr_next = tape.centered_player_nr_next;
11801     game.set_centered_player = TRUE;
11802   }
11803
11804   for (i = 0; i < MAX_PLAYERS; i++)
11805   {
11806     summarized_player_action |= stored_player[i].action;
11807
11808     if (!network_playing && (game.team_mode || tape.playing))
11809       stored_player[i].effective_action = stored_player[i].action;
11810   }
11811
11812   if (network_playing && !checkGameEnded())
11813     SendToServer_MovePlayer(summarized_player_action);
11814
11815   // summarize all actions at local players mapped input device position
11816   // (this allows using different input devices in single player mode)
11817   if (!network.enabled && !game.team_mode)
11818     stored_player[map_player_action[local_player->index_nr]].effective_action =
11819       summarized_player_action;
11820
11821   // summarize all actions at centered player in local team mode
11822   if (tape.recording &&
11823       setup.team_mode && !network.enabled &&
11824       setup.input_on_focus &&
11825       game.centered_player_nr != -1)
11826   {
11827     for (i = 0; i < MAX_PLAYERS; i++)
11828       stored_player[map_player_action[i]].effective_action =
11829         (i == game.centered_player_nr ? summarized_player_action : 0);
11830   }
11831
11832   if (recorded_player_action != NULL)
11833     for (i = 0; i < MAX_PLAYERS; i++)
11834       stored_player[i].effective_action = recorded_player_action[i];
11835
11836   for (i = 0; i < MAX_PLAYERS; i++)
11837   {
11838     tape_action[i] = stored_player[i].effective_action;
11839
11840     /* (this may happen in the RND game engine if a player was not present on
11841        the playfield on level start, but appeared later from a custom element */
11842     if (setup.team_mode &&
11843         tape.recording &&
11844         tape_action[i] &&
11845         !tape.player_participates[i])
11846       tape.player_participates[i] = TRUE;
11847   }
11848
11849   SetTapeActionFromMouseAction(tape_action,
11850                                &local_player->effective_mouse_action);
11851
11852   // only record actions from input devices, but not programmed actions
11853   if (tape.recording)
11854     TapeRecordAction(tape_action);
11855
11856   // remember if game was played (especially after tape stopped playing)
11857   if (!tape.playing && summarized_player_action)
11858     game.GamePlayed = TRUE;
11859
11860 #if USE_NEW_PLAYER_ASSIGNMENTS
11861   // !!! also map player actions in single player mode !!!
11862   // if (game.team_mode)
11863   if (1)
11864   {
11865     byte mapped_action[MAX_PLAYERS];
11866
11867 #if DEBUG_PLAYER_ACTIONS
11868     for (i = 0; i < MAX_PLAYERS; i++)
11869       DebugContinued("", "%d, ", stored_player[i].effective_action);
11870 #endif
11871
11872     for (i = 0; i < MAX_PLAYERS; i++)
11873       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11874
11875     for (i = 0; i < MAX_PLAYERS; i++)
11876       stored_player[i].effective_action = mapped_action[i];
11877
11878 #if DEBUG_PLAYER_ACTIONS
11879     DebugContinued("", "=> ");
11880     for (i = 0; i < MAX_PLAYERS; i++)
11881       DebugContinued("", "%d, ", stored_player[i].effective_action);
11882     DebugContinued("game:playing:player", "\n");
11883 #endif
11884   }
11885 #if DEBUG_PLAYER_ACTIONS
11886   else
11887   {
11888     for (i = 0; i < MAX_PLAYERS; i++)
11889       DebugContinued("", "%d, ", stored_player[i].effective_action);
11890     DebugContinued("game:playing:player", "\n");
11891   }
11892 #endif
11893 #endif
11894
11895   for (i = 0; i < MAX_PLAYERS; i++)
11896   {
11897     // allow engine snapshot in case of changed movement attempt
11898     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11899         (stored_player[i].effective_action & KEY_MOTION))
11900       game.snapshot.changed_action = TRUE;
11901
11902     // allow engine snapshot in case of snapping/dropping attempt
11903     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11904         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11905       game.snapshot.changed_action = TRUE;
11906
11907     game.snapshot.last_action[i] = stored_player[i].effective_action;
11908   }
11909
11910   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11911   {
11912     GameActions_EM_Main();
11913   }
11914   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11915   {
11916     GameActions_SP_Main();
11917   }
11918   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11919   {
11920     GameActions_MM_Main();
11921   }
11922   else
11923   {
11924     GameActions_RND_Main();
11925   }
11926
11927   BlitScreenToBitmap(backbuffer);
11928
11929   CheckLevelSolved();
11930   CheckLevelTime();
11931
11932   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11933
11934   if (global.show_frames_per_second)
11935   {
11936     static unsigned int fps_counter = 0;
11937     static int fps_frames = 0;
11938     unsigned int fps_delay_ms = Counter() - fps_counter;
11939
11940     fps_frames++;
11941
11942     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11943     {
11944       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11945
11946       fps_frames = 0;
11947       fps_counter = Counter();
11948
11949       // always draw FPS to screen after FPS value was updated
11950       redraw_mask |= REDRAW_FPS;
11951     }
11952
11953     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11954     if (GetDrawDeactivationMask() == REDRAW_NONE)
11955       redraw_mask |= REDRAW_FPS;
11956   }
11957 }
11958
11959 static void GameActions_CheckSaveEngineSnapshot(void)
11960 {
11961   if (!game.snapshot.save_snapshot)
11962     return;
11963
11964   // clear flag for saving snapshot _before_ saving snapshot
11965   game.snapshot.save_snapshot = FALSE;
11966
11967   SaveEngineSnapshotToList();
11968 }
11969
11970 void GameActions(void)
11971 {
11972   GameActionsExt();
11973
11974   GameActions_CheckSaveEngineSnapshot();
11975 }
11976
11977 void GameActions_EM_Main(void)
11978 {
11979   byte effective_action[MAX_PLAYERS];
11980   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11981   int i;
11982
11983   for (i = 0; i < MAX_PLAYERS; i++)
11984     effective_action[i] = stored_player[i].effective_action;
11985
11986   GameActions_EM(effective_action, warp_mode);
11987 }
11988
11989 void GameActions_SP_Main(void)
11990 {
11991   byte effective_action[MAX_PLAYERS];
11992   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11993   int i;
11994
11995   for (i = 0; i < MAX_PLAYERS; i++)
11996     effective_action[i] = stored_player[i].effective_action;
11997
11998   GameActions_SP(effective_action, warp_mode);
11999
12000   for (i = 0; i < MAX_PLAYERS; i++)
12001   {
12002     if (stored_player[i].force_dropping)
12003       stored_player[i].action |= KEY_BUTTON_DROP;
12004
12005     stored_player[i].force_dropping = FALSE;
12006   }
12007 }
12008
12009 void GameActions_MM_Main(void)
12010 {
12011   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12012
12013   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12014 }
12015
12016 void GameActions_RND_Main(void)
12017 {
12018   GameActions_RND();
12019 }
12020
12021 void GameActions_RND(void)
12022 {
12023   static struct MouseActionInfo mouse_action_last = { 0 };
12024   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12025   int magic_wall_x = 0, magic_wall_y = 0;
12026   int i, x, y, element, graphic, last_gfx_frame;
12027
12028   InitPlayfieldScanModeVars();
12029
12030   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12031   {
12032     SCAN_PLAYFIELD(x, y)
12033     {
12034       ChangeCount[x][y] = 0;
12035       ChangeEvent[x][y] = -1;
12036     }
12037   }
12038
12039   if (game.set_centered_player)
12040   {
12041     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12042
12043     // switching to "all players" only possible if all players fit to screen
12044     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12045     {
12046       game.centered_player_nr_next = game.centered_player_nr;
12047       game.set_centered_player = FALSE;
12048     }
12049
12050     // do not switch focus to non-existing (or non-active) player
12051     if (game.centered_player_nr_next >= 0 &&
12052         !stored_player[game.centered_player_nr_next].active)
12053     {
12054       game.centered_player_nr_next = game.centered_player_nr;
12055       game.set_centered_player = FALSE;
12056     }
12057   }
12058
12059   if (game.set_centered_player &&
12060       ScreenMovPos == 0)        // screen currently aligned at tile position
12061   {
12062     int sx, sy;
12063
12064     if (game.centered_player_nr_next == -1)
12065     {
12066       setScreenCenteredToAllPlayers(&sx, &sy);
12067     }
12068     else
12069     {
12070       sx = stored_player[game.centered_player_nr_next].jx;
12071       sy = stored_player[game.centered_player_nr_next].jy;
12072     }
12073
12074     game.centered_player_nr = game.centered_player_nr_next;
12075     game.set_centered_player = FALSE;
12076
12077     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12078     DrawGameDoorValues();
12079   }
12080
12081   // check single step mode (set flag and clear again if any player is active)
12082   game.enter_single_step_mode =
12083     (tape.single_step && tape.recording && !tape.pausing);
12084
12085   for (i = 0; i < MAX_PLAYERS; i++)
12086   {
12087     int actual_player_action = stored_player[i].effective_action;
12088
12089 #if 1
12090     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12091        - rnd_equinox_tetrachloride 048
12092        - rnd_equinox_tetrachloride_ii 096
12093        - rnd_emanuel_schmieg 002
12094        - doctor_sloan_ww 001, 020
12095     */
12096     if (stored_player[i].MovPos == 0)
12097       CheckGravityMovement(&stored_player[i]);
12098 #endif
12099
12100     // overwrite programmed action with tape action
12101     if (stored_player[i].programmed_action)
12102       actual_player_action = stored_player[i].programmed_action;
12103
12104     PlayerActions(&stored_player[i], actual_player_action);
12105
12106     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12107   }
12108
12109   // single step pause mode may already have been toggled by "ScrollPlayer()"
12110   if (game.enter_single_step_mode && !tape.pausing)
12111     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12112
12113   ScrollScreen(NULL, SCROLL_GO_ON);
12114
12115   /* for backwards compatibility, the following code emulates a fixed bug that
12116      occured when pushing elements (causing elements that just made their last
12117      pushing step to already (if possible) make their first falling step in the
12118      same game frame, which is bad); this code is also needed to use the famous
12119      "spring push bug" which is used in older levels and might be wanted to be
12120      used also in newer levels, but in this case the buggy pushing code is only
12121      affecting the "spring" element and no other elements */
12122
12123   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12124   {
12125     for (i = 0; i < MAX_PLAYERS; i++)
12126     {
12127       struct PlayerInfo *player = &stored_player[i];
12128       int x = player->jx;
12129       int y = player->jy;
12130
12131       if (player->active && player->is_pushing && player->is_moving &&
12132           IS_MOVING(x, y) &&
12133           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12134            Tile[x][y] == EL_SPRING))
12135       {
12136         ContinueMoving(x, y);
12137
12138         // continue moving after pushing (this is actually a bug)
12139         if (!IS_MOVING(x, y))
12140           Stop[x][y] = FALSE;
12141       }
12142     }
12143   }
12144
12145   SCAN_PLAYFIELD(x, y)
12146   {
12147     Last[x][y] = Tile[x][y];
12148
12149     ChangeCount[x][y] = 0;
12150     ChangeEvent[x][y] = -1;
12151
12152     // this must be handled before main playfield loop
12153     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12154     {
12155       MovDelay[x][y]--;
12156       if (MovDelay[x][y] <= 0)
12157         RemoveField(x, y);
12158     }
12159
12160     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12161     {
12162       MovDelay[x][y]--;
12163       if (MovDelay[x][y] <= 0)
12164       {
12165         int element = Store[x][y];
12166         int move_direction = MovDir[x][y];
12167         int player_index_bit = Store2[x][y];
12168
12169         Store[x][y] = 0;
12170         Store2[x][y] = 0;
12171
12172         RemoveField(x, y);
12173         TEST_DrawLevelField(x, y);
12174
12175         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12176
12177         if (IS_ENVELOPE(element))
12178           local_player->show_envelope = element;
12179       }
12180     }
12181
12182 #if DEBUG
12183     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12184     {
12185       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12186             x, y);
12187       Debug("game:playing:GameActions_RND", "This should never happen!");
12188
12189       ChangePage[x][y] = -1;
12190     }
12191 #endif
12192
12193     Stop[x][y] = FALSE;
12194     if (WasJustMoving[x][y] > 0)
12195       WasJustMoving[x][y]--;
12196     if (WasJustFalling[x][y] > 0)
12197       WasJustFalling[x][y]--;
12198     if (CheckCollision[x][y] > 0)
12199       CheckCollision[x][y]--;
12200     if (CheckImpact[x][y] > 0)
12201       CheckImpact[x][y]--;
12202
12203     GfxFrame[x][y]++;
12204
12205     /* reset finished pushing action (not done in ContinueMoving() to allow
12206        continuous pushing animation for elements with zero push delay) */
12207     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12208     {
12209       ResetGfxAnimation(x, y);
12210       TEST_DrawLevelField(x, y);
12211     }
12212
12213 #if DEBUG
12214     if (IS_BLOCKED(x, y))
12215     {
12216       int oldx, oldy;
12217
12218       Blocked2Moving(x, y, &oldx, &oldy);
12219       if (!IS_MOVING(oldx, oldy))
12220       {
12221         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12222         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12223         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12224         Debug("game:playing:GameActions_RND", "This should never happen!");
12225       }
12226     }
12227 #endif
12228   }
12229
12230   if (mouse_action.button)
12231   {
12232     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12233     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12234
12235     x = mouse_action.lx;
12236     y = mouse_action.ly;
12237     element = Tile[x][y];
12238
12239     if (new_button)
12240     {
12241       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12242       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12243                                          ch_button);
12244     }
12245
12246     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12247     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12248                                        ch_button);
12249   }
12250
12251   SCAN_PLAYFIELD(x, y)
12252   {
12253     element = Tile[x][y];
12254     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12255     last_gfx_frame = GfxFrame[x][y];
12256
12257     ResetGfxFrame(x, y);
12258
12259     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12260       DrawLevelGraphicAnimation(x, y, graphic);
12261
12262     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12263         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12264       ResetRandomAnimationValue(x, y);
12265
12266     SetRandomAnimationValue(x, y);
12267
12268     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12269
12270     if (IS_INACTIVE(element))
12271     {
12272       if (IS_ANIMATED(graphic))
12273         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12274
12275       continue;
12276     }
12277
12278     // this may take place after moving, so 'element' may have changed
12279     if (IS_CHANGING(x, y) &&
12280         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12281     {
12282       int page = element_info[element].event_page_nr[CE_DELAY];
12283
12284       HandleElementChange(x, y, page);
12285
12286       element = Tile[x][y];
12287       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12288     }
12289
12290     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12291     {
12292       StartMoving(x, y);
12293
12294       element = Tile[x][y];
12295       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12296
12297       if (IS_ANIMATED(graphic) &&
12298           !IS_MOVING(x, y) &&
12299           !Stop[x][y])
12300         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12301
12302       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12303         TEST_DrawTwinkleOnField(x, y);
12304     }
12305     else if (element == EL_ACID)
12306     {
12307       if (!Stop[x][y])
12308         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12309     }
12310     else if ((element == EL_EXIT_OPEN ||
12311               element == EL_EM_EXIT_OPEN ||
12312               element == EL_SP_EXIT_OPEN ||
12313               element == EL_STEEL_EXIT_OPEN ||
12314               element == EL_EM_STEEL_EXIT_OPEN ||
12315               element == EL_SP_TERMINAL ||
12316               element == EL_SP_TERMINAL_ACTIVE ||
12317               element == EL_EXTRA_TIME ||
12318               element == EL_SHIELD_NORMAL ||
12319               element == EL_SHIELD_DEADLY) &&
12320              IS_ANIMATED(graphic))
12321       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12322     else if (IS_MOVING(x, y))
12323       ContinueMoving(x, y);
12324     else if (IS_ACTIVE_BOMB(element))
12325       CheckDynamite(x, y);
12326     else if (element == EL_AMOEBA_GROWING)
12327       AmoebaGrowing(x, y);
12328     else if (element == EL_AMOEBA_SHRINKING)
12329       AmoebaShrinking(x, y);
12330
12331 #if !USE_NEW_AMOEBA_CODE
12332     else if (IS_AMOEBALIVE(element))
12333       AmoebaReproduce(x, y);
12334 #endif
12335
12336     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12337       Life(x, y);
12338     else if (element == EL_EXIT_CLOSED)
12339       CheckExit(x, y);
12340     else if (element == EL_EM_EXIT_CLOSED)
12341       CheckExitEM(x, y);
12342     else if (element == EL_STEEL_EXIT_CLOSED)
12343       CheckExitSteel(x, y);
12344     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12345       CheckExitSteelEM(x, y);
12346     else if (element == EL_SP_EXIT_CLOSED)
12347       CheckExitSP(x, y);
12348     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12349              element == EL_EXPANDABLE_STEELWALL_GROWING)
12350       MauerWaechst(x, y);
12351     else if (element == EL_EXPANDABLE_WALL ||
12352              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12353              element == EL_EXPANDABLE_WALL_VERTICAL ||
12354              element == EL_EXPANDABLE_WALL_ANY ||
12355              element == EL_BD_EXPANDABLE_WALL)
12356       MauerAbleger(x, y);
12357     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12358              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12359              element == EL_EXPANDABLE_STEELWALL_ANY)
12360       MauerAblegerStahl(x, y);
12361     else if (element == EL_FLAMES)
12362       CheckForDragon(x, y);
12363     else if (element == EL_EXPLOSION)
12364       ; // drawing of correct explosion animation is handled separately
12365     else if (element == EL_ELEMENT_SNAPPING ||
12366              element == EL_DIAGONAL_SHRINKING ||
12367              element == EL_DIAGONAL_GROWING)
12368     {
12369       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12370
12371       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12372     }
12373     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12374       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12375
12376     if (IS_BELT_ACTIVE(element))
12377       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12378
12379     if (game.magic_wall_active)
12380     {
12381       int jx = local_player->jx, jy = local_player->jy;
12382
12383       // play the element sound at the position nearest to the player
12384       if ((element == EL_MAGIC_WALL_FULL ||
12385            element == EL_MAGIC_WALL_ACTIVE ||
12386            element == EL_MAGIC_WALL_EMPTYING ||
12387            element == EL_BD_MAGIC_WALL_FULL ||
12388            element == EL_BD_MAGIC_WALL_ACTIVE ||
12389            element == EL_BD_MAGIC_WALL_EMPTYING ||
12390            element == EL_DC_MAGIC_WALL_FULL ||
12391            element == EL_DC_MAGIC_WALL_ACTIVE ||
12392            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12393           ABS(x - jx) + ABS(y - jy) <
12394           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12395       {
12396         magic_wall_x = x;
12397         magic_wall_y = y;
12398       }
12399     }
12400   }
12401
12402 #if USE_NEW_AMOEBA_CODE
12403   // new experimental amoeba growth stuff
12404   if (!(FrameCounter % 8))
12405   {
12406     static unsigned int random = 1684108901;
12407
12408     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12409     {
12410       x = RND(lev_fieldx);
12411       y = RND(lev_fieldy);
12412       element = Tile[x][y];
12413
12414       if (!IS_PLAYER(x,y) &&
12415           (element == EL_EMPTY ||
12416            CAN_GROW_INTO(element) ||
12417            element == EL_QUICKSAND_EMPTY ||
12418            element == EL_QUICKSAND_FAST_EMPTY ||
12419            element == EL_ACID_SPLASH_LEFT ||
12420            element == EL_ACID_SPLASH_RIGHT))
12421       {
12422         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12423             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12424             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12425             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12426           Tile[x][y] = EL_AMOEBA_DROP;
12427       }
12428
12429       random = random * 129 + 1;
12430     }
12431   }
12432 #endif
12433
12434   game.explosions_delayed = FALSE;
12435
12436   SCAN_PLAYFIELD(x, y)
12437   {
12438     element = Tile[x][y];
12439
12440     if (ExplodeField[x][y])
12441       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12442     else if (element == EL_EXPLOSION)
12443       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12444
12445     ExplodeField[x][y] = EX_TYPE_NONE;
12446   }
12447
12448   game.explosions_delayed = TRUE;
12449
12450   if (game.magic_wall_active)
12451   {
12452     if (!(game.magic_wall_time_left % 4))
12453     {
12454       int element = Tile[magic_wall_x][magic_wall_y];
12455
12456       if (element == EL_BD_MAGIC_WALL_FULL ||
12457           element == EL_BD_MAGIC_WALL_ACTIVE ||
12458           element == EL_BD_MAGIC_WALL_EMPTYING)
12459         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12460       else if (element == EL_DC_MAGIC_WALL_FULL ||
12461                element == EL_DC_MAGIC_WALL_ACTIVE ||
12462                element == EL_DC_MAGIC_WALL_EMPTYING)
12463         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12464       else
12465         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12466     }
12467
12468     if (game.magic_wall_time_left > 0)
12469     {
12470       game.magic_wall_time_left--;
12471
12472       if (!game.magic_wall_time_left)
12473       {
12474         SCAN_PLAYFIELD(x, y)
12475         {
12476           element = Tile[x][y];
12477
12478           if (element == EL_MAGIC_WALL_ACTIVE ||
12479               element == EL_MAGIC_WALL_FULL)
12480           {
12481             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12482             TEST_DrawLevelField(x, y);
12483           }
12484           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12485                    element == EL_BD_MAGIC_WALL_FULL)
12486           {
12487             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12488             TEST_DrawLevelField(x, y);
12489           }
12490           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12491                    element == EL_DC_MAGIC_WALL_FULL)
12492           {
12493             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12494             TEST_DrawLevelField(x, y);
12495           }
12496         }
12497
12498         game.magic_wall_active = FALSE;
12499       }
12500     }
12501   }
12502
12503   if (game.light_time_left > 0)
12504   {
12505     game.light_time_left--;
12506
12507     if (game.light_time_left == 0)
12508       RedrawAllLightSwitchesAndInvisibleElements();
12509   }
12510
12511   if (game.timegate_time_left > 0)
12512   {
12513     game.timegate_time_left--;
12514
12515     if (game.timegate_time_left == 0)
12516       CloseAllOpenTimegates();
12517   }
12518
12519   if (game.lenses_time_left > 0)
12520   {
12521     game.lenses_time_left--;
12522
12523     if (game.lenses_time_left == 0)
12524       RedrawAllInvisibleElementsForLenses();
12525   }
12526
12527   if (game.magnify_time_left > 0)
12528   {
12529     game.magnify_time_left--;
12530
12531     if (game.magnify_time_left == 0)
12532       RedrawAllInvisibleElementsForMagnifier();
12533   }
12534
12535   for (i = 0; i < MAX_PLAYERS; i++)
12536   {
12537     struct PlayerInfo *player = &stored_player[i];
12538
12539     if (SHIELD_ON(player))
12540     {
12541       if (player->shield_deadly_time_left)
12542         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12543       else if (player->shield_normal_time_left)
12544         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12545     }
12546   }
12547
12548 #if USE_DELAYED_GFX_REDRAW
12549   SCAN_PLAYFIELD(x, y)
12550   {
12551     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12552     {
12553       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12554          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12555
12556       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12557         DrawLevelField(x, y);
12558
12559       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12560         DrawLevelFieldCrumbled(x, y);
12561
12562       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12563         DrawLevelFieldCrumbledNeighbours(x, y);
12564
12565       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12566         DrawTwinkleOnField(x, y);
12567     }
12568
12569     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12570   }
12571 #endif
12572
12573   DrawAllPlayers();
12574   PlayAllPlayersSound();
12575
12576   for (i = 0; i < MAX_PLAYERS; i++)
12577   {
12578     struct PlayerInfo *player = &stored_player[i];
12579
12580     if (player->show_envelope != 0 && (!player->active ||
12581                                        player->MovPos == 0))
12582     {
12583       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12584
12585       player->show_envelope = 0;
12586     }
12587   }
12588
12589   // use random number generator in every frame to make it less predictable
12590   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12591     RND(1);
12592
12593   mouse_action_last = mouse_action;
12594 }
12595
12596 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12597 {
12598   int min_x = x, min_y = y, max_x = x, max_y = y;
12599   int scr_fieldx = getScreenFieldSizeX();
12600   int scr_fieldy = getScreenFieldSizeY();
12601   int i;
12602
12603   for (i = 0; i < MAX_PLAYERS; i++)
12604   {
12605     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12606
12607     if (!stored_player[i].active || &stored_player[i] == player)
12608       continue;
12609
12610     min_x = MIN(min_x, jx);
12611     min_y = MIN(min_y, jy);
12612     max_x = MAX(max_x, jx);
12613     max_y = MAX(max_y, jy);
12614   }
12615
12616   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12617 }
12618
12619 static boolean AllPlayersInVisibleScreen(void)
12620 {
12621   int i;
12622
12623   for (i = 0; i < MAX_PLAYERS; i++)
12624   {
12625     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12626
12627     if (!stored_player[i].active)
12628       continue;
12629
12630     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12631       return FALSE;
12632   }
12633
12634   return TRUE;
12635 }
12636
12637 void ScrollLevel(int dx, int dy)
12638 {
12639   int scroll_offset = 2 * TILEX_VAR;
12640   int x, y;
12641
12642   BlitBitmap(drawto_field, drawto_field,
12643              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12644              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12645              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12646              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12647              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12648              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12649
12650   if (dx != 0)
12651   {
12652     x = (dx == 1 ? BX1 : BX2);
12653     for (y = BY1; y <= BY2; y++)
12654       DrawScreenField(x, y);
12655   }
12656
12657   if (dy != 0)
12658   {
12659     y = (dy == 1 ? BY1 : BY2);
12660     for (x = BX1; x <= BX2; x++)
12661       DrawScreenField(x, y);
12662   }
12663
12664   redraw_mask |= REDRAW_FIELD;
12665 }
12666
12667 static boolean canFallDown(struct PlayerInfo *player)
12668 {
12669   int jx = player->jx, jy = player->jy;
12670
12671   return (IN_LEV_FIELD(jx, jy + 1) &&
12672           (IS_FREE(jx, jy + 1) ||
12673            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12674           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12675           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12676 }
12677
12678 static boolean canPassField(int x, int y, int move_dir)
12679 {
12680   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12681   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12682   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12683   int nextx = x + dx;
12684   int nexty = y + dy;
12685   int element = Tile[x][y];
12686
12687   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12688           !CAN_MOVE(element) &&
12689           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12690           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12691           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12692 }
12693
12694 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12695 {
12696   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12697   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12698   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12699   int newx = x + dx;
12700   int newy = y + dy;
12701
12702   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12703           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12704           (IS_DIGGABLE(Tile[newx][newy]) ||
12705            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12706            canPassField(newx, newy, move_dir)));
12707 }
12708
12709 static void CheckGravityMovement(struct PlayerInfo *player)
12710 {
12711   if (player->gravity && !player->programmed_action)
12712   {
12713     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12714     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12715     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12716     int jx = player->jx, jy = player->jy;
12717     boolean player_is_moving_to_valid_field =
12718       (!player_is_snapping &&
12719        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12720         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12721     boolean player_can_fall_down = canFallDown(player);
12722
12723     if (player_can_fall_down &&
12724         !player_is_moving_to_valid_field)
12725       player->programmed_action = MV_DOWN;
12726   }
12727 }
12728
12729 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12730 {
12731   return CheckGravityMovement(player);
12732
12733   if (player->gravity && !player->programmed_action)
12734   {
12735     int jx = player->jx, jy = player->jy;
12736     boolean field_under_player_is_free =
12737       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12738     boolean player_is_standing_on_valid_field =
12739       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12740        (IS_WALKABLE(Tile[jx][jy]) &&
12741         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12742
12743     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12744       player->programmed_action = MV_DOWN;
12745   }
12746 }
12747
12748 /*
12749   MovePlayerOneStep()
12750   -----------------------------------------------------------------------------
12751   dx, dy:               direction (non-diagonal) to try to move the player to
12752   real_dx, real_dy:     direction as read from input device (can be diagonal)
12753 */
12754
12755 boolean MovePlayerOneStep(struct PlayerInfo *player,
12756                           int dx, int dy, int real_dx, int real_dy)
12757 {
12758   int jx = player->jx, jy = player->jy;
12759   int new_jx = jx + dx, new_jy = jy + dy;
12760   int can_move;
12761   boolean player_can_move = !player->cannot_move;
12762
12763   if (!player->active || (!dx && !dy))
12764     return MP_NO_ACTION;
12765
12766   player->MovDir = (dx < 0 ? MV_LEFT :
12767                     dx > 0 ? MV_RIGHT :
12768                     dy < 0 ? MV_UP :
12769                     dy > 0 ? MV_DOWN :  MV_NONE);
12770
12771   if (!IN_LEV_FIELD(new_jx, new_jy))
12772     return MP_NO_ACTION;
12773
12774   if (!player_can_move)
12775   {
12776     if (player->MovPos == 0)
12777     {
12778       player->is_moving = FALSE;
12779       player->is_digging = FALSE;
12780       player->is_collecting = FALSE;
12781       player->is_snapping = FALSE;
12782       player->is_pushing = FALSE;
12783     }
12784   }
12785
12786   if (!network.enabled && game.centered_player_nr == -1 &&
12787       !AllPlayersInSight(player, new_jx, new_jy))
12788     return MP_NO_ACTION;
12789
12790   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12791   if (can_move != MP_MOVING)
12792     return can_move;
12793
12794   // check if DigField() has caused relocation of the player
12795   if (player->jx != jx || player->jy != jy)
12796     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12797
12798   StorePlayer[jx][jy] = 0;
12799   player->last_jx = jx;
12800   player->last_jy = jy;
12801   player->jx = new_jx;
12802   player->jy = new_jy;
12803   StorePlayer[new_jx][new_jy] = player->element_nr;
12804
12805   if (player->move_delay_value_next != -1)
12806   {
12807     player->move_delay_value = player->move_delay_value_next;
12808     player->move_delay_value_next = -1;
12809   }
12810
12811   player->MovPos =
12812     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12813
12814   player->step_counter++;
12815
12816   PlayerVisit[jx][jy] = FrameCounter;
12817
12818   player->is_moving = TRUE;
12819
12820 #if 1
12821   // should better be called in MovePlayer(), but this breaks some tapes
12822   ScrollPlayer(player, SCROLL_INIT);
12823 #endif
12824
12825   return MP_MOVING;
12826 }
12827
12828 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12829 {
12830   int jx = player->jx, jy = player->jy;
12831   int old_jx = jx, old_jy = jy;
12832   int moved = MP_NO_ACTION;
12833
12834   if (!player->active)
12835     return FALSE;
12836
12837   if (!dx && !dy)
12838   {
12839     if (player->MovPos == 0)
12840     {
12841       player->is_moving = FALSE;
12842       player->is_digging = FALSE;
12843       player->is_collecting = FALSE;
12844       player->is_snapping = FALSE;
12845       player->is_pushing = FALSE;
12846     }
12847
12848     return FALSE;
12849   }
12850
12851   if (player->move_delay > 0)
12852     return FALSE;
12853
12854   player->move_delay = -1;              // set to "uninitialized" value
12855
12856   // store if player is automatically moved to next field
12857   player->is_auto_moving = (player->programmed_action != MV_NONE);
12858
12859   // remove the last programmed player action
12860   player->programmed_action = 0;
12861
12862   if (player->MovPos)
12863   {
12864     // should only happen if pre-1.2 tape recordings are played
12865     // this is only for backward compatibility
12866
12867     int original_move_delay_value = player->move_delay_value;
12868
12869 #if DEBUG
12870     Debug("game:playing:MovePlayer",
12871           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12872           tape.counter);
12873 #endif
12874
12875     // scroll remaining steps with finest movement resolution
12876     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12877
12878     while (player->MovPos)
12879     {
12880       ScrollPlayer(player, SCROLL_GO_ON);
12881       ScrollScreen(NULL, SCROLL_GO_ON);
12882
12883       AdvanceFrameAndPlayerCounters(player->index_nr);
12884
12885       DrawAllPlayers();
12886       BackToFront_WithFrameDelay(0);
12887     }
12888
12889     player->move_delay_value = original_move_delay_value;
12890   }
12891
12892   player->is_active = FALSE;
12893
12894   if (player->last_move_dir & MV_HORIZONTAL)
12895   {
12896     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12897       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12898   }
12899   else
12900   {
12901     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12902       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12903   }
12904
12905   if (!moved && !player->is_active)
12906   {
12907     player->is_moving = FALSE;
12908     player->is_digging = FALSE;
12909     player->is_collecting = FALSE;
12910     player->is_snapping = FALSE;
12911     player->is_pushing = FALSE;
12912   }
12913
12914   jx = player->jx;
12915   jy = player->jy;
12916
12917   if (moved & MP_MOVING && !ScreenMovPos &&
12918       (player->index_nr == game.centered_player_nr ||
12919        game.centered_player_nr == -1))
12920   {
12921     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12922
12923     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12924     {
12925       // actual player has left the screen -- scroll in that direction
12926       if (jx != old_jx)         // player has moved horizontally
12927         scroll_x += (jx - old_jx);
12928       else                      // player has moved vertically
12929         scroll_y += (jy - old_jy);
12930     }
12931     else
12932     {
12933       int offset_raw = game.scroll_delay_value;
12934
12935       if (jx != old_jx)         // player has moved horizontally
12936       {
12937         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12938         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12939         int new_scroll_x = jx - MIDPOSX + offset_x;
12940
12941         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12942             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12943           scroll_x = new_scroll_x;
12944
12945         // don't scroll over playfield boundaries
12946         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12947
12948         // don't scroll more than one field at a time
12949         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12950
12951         // don't scroll against the player's moving direction
12952         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12953             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12954           scroll_x = old_scroll_x;
12955       }
12956       else                      // player has moved vertically
12957       {
12958         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12959         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12960         int new_scroll_y = jy - MIDPOSY + offset_y;
12961
12962         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12963             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12964           scroll_y = new_scroll_y;
12965
12966         // don't scroll over playfield boundaries
12967         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12968
12969         // don't scroll more than one field at a time
12970         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12971
12972         // don't scroll against the player's moving direction
12973         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12974             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12975           scroll_y = old_scroll_y;
12976       }
12977     }
12978
12979     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12980     {
12981       if (!network.enabled && game.centered_player_nr == -1 &&
12982           !AllPlayersInVisibleScreen())
12983       {
12984         scroll_x = old_scroll_x;
12985         scroll_y = old_scroll_y;
12986       }
12987       else
12988       {
12989         ScrollScreen(player, SCROLL_INIT);
12990         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12991       }
12992     }
12993   }
12994
12995   player->StepFrame = 0;
12996
12997   if (moved & MP_MOVING)
12998   {
12999     if (old_jx != jx && old_jy == jy)
13000       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13001     else if (old_jx == jx && old_jy != jy)
13002       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13003
13004     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13005
13006     player->last_move_dir = player->MovDir;
13007     player->is_moving = TRUE;
13008     player->is_snapping = FALSE;
13009     player->is_switching = FALSE;
13010     player->is_dropping = FALSE;
13011     player->is_dropping_pressed = FALSE;
13012     player->drop_pressed_delay = 0;
13013
13014 #if 0
13015     // should better be called here than above, but this breaks some tapes
13016     ScrollPlayer(player, SCROLL_INIT);
13017 #endif
13018   }
13019   else
13020   {
13021     CheckGravityMovementWhenNotMoving(player);
13022
13023     player->is_moving = FALSE;
13024
13025     /* at this point, the player is allowed to move, but cannot move right now
13026        (e.g. because of something blocking the way) -- ensure that the player
13027        is also allowed to move in the next frame (in old versions before 3.1.1,
13028        the player was forced to wait again for eight frames before next try) */
13029
13030     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13031       player->move_delay = 0;   // allow direct movement in the next frame
13032   }
13033
13034   if (player->move_delay == -1)         // not yet initialized by DigField()
13035     player->move_delay = player->move_delay_value;
13036
13037   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13038   {
13039     TestIfPlayerTouchesBadThing(jx, jy);
13040     TestIfPlayerTouchesCustomElement(jx, jy);
13041   }
13042
13043   if (!player->active)
13044     RemovePlayer(player);
13045
13046   return moved;
13047 }
13048
13049 void ScrollPlayer(struct PlayerInfo *player, int mode)
13050 {
13051   int jx = player->jx, jy = player->jy;
13052   int last_jx = player->last_jx, last_jy = player->last_jy;
13053   int move_stepsize = TILEX / player->move_delay_value;
13054
13055   if (!player->active)
13056     return;
13057
13058   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13059     return;
13060
13061   if (mode == SCROLL_INIT)
13062   {
13063     player->actual_frame_counter = FrameCounter;
13064     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13065
13066     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13067         Tile[last_jx][last_jy] == EL_EMPTY)
13068     {
13069       int last_field_block_delay = 0;   // start with no blocking at all
13070       int block_delay_adjustment = player->block_delay_adjustment;
13071
13072       // if player blocks last field, add delay for exactly one move
13073       if (player->block_last_field)
13074       {
13075         last_field_block_delay += player->move_delay_value;
13076
13077         // when blocking enabled, prevent moving up despite gravity
13078         if (player->gravity && player->MovDir == MV_UP)
13079           block_delay_adjustment = -1;
13080       }
13081
13082       // add block delay adjustment (also possible when not blocking)
13083       last_field_block_delay += block_delay_adjustment;
13084
13085       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13086       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13087     }
13088
13089     if (player->MovPos != 0)    // player has not yet reached destination
13090       return;
13091   }
13092   else if (!FrameReached(&player->actual_frame_counter, 1))
13093     return;
13094
13095   if (player->MovPos != 0)
13096   {
13097     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13098     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13099
13100     // before DrawPlayer() to draw correct player graphic for this case
13101     if (player->MovPos == 0)
13102       CheckGravityMovement(player);
13103   }
13104
13105   if (player->MovPos == 0)      // player reached destination field
13106   {
13107     if (player->move_delay_reset_counter > 0)
13108     {
13109       player->move_delay_reset_counter--;
13110
13111       if (player->move_delay_reset_counter == 0)
13112       {
13113         // continue with normal speed after quickly moving through gate
13114         HALVE_PLAYER_SPEED(player);
13115
13116         // be able to make the next move without delay
13117         player->move_delay = 0;
13118       }
13119     }
13120
13121     player->last_jx = jx;
13122     player->last_jy = jy;
13123
13124     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13125         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13126         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13127         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13128         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13129         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13130         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13131         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13132     {
13133       ExitPlayer(player);
13134
13135       if (game.players_still_needed == 0 &&
13136           (game.friends_still_needed == 0 ||
13137            IS_SP_ELEMENT(Tile[jx][jy])))
13138         LevelSolved();
13139     }
13140
13141     // this breaks one level: "machine", level 000
13142     {
13143       int move_direction = player->MovDir;
13144       int enter_side = MV_DIR_OPPOSITE(move_direction);
13145       int leave_side = move_direction;
13146       int old_jx = last_jx;
13147       int old_jy = last_jy;
13148       int old_element = Tile[old_jx][old_jy];
13149       int new_element = Tile[jx][jy];
13150
13151       if (IS_CUSTOM_ELEMENT(old_element))
13152         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13153                                    CE_LEFT_BY_PLAYER,
13154                                    player->index_bit, leave_side);
13155
13156       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13157                                           CE_PLAYER_LEAVES_X,
13158                                           player->index_bit, leave_side);
13159
13160       if (IS_CUSTOM_ELEMENT(new_element))
13161         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13162                                    player->index_bit, enter_side);
13163
13164       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13165                                           CE_PLAYER_ENTERS_X,
13166                                           player->index_bit, enter_side);
13167
13168       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13169                                         CE_MOVE_OF_X, move_direction);
13170     }
13171
13172     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13173     {
13174       TestIfPlayerTouchesBadThing(jx, jy);
13175       TestIfPlayerTouchesCustomElement(jx, jy);
13176
13177       /* needed because pushed element has not yet reached its destination,
13178          so it would trigger a change event at its previous field location */
13179       if (!player->is_pushing)
13180         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13181
13182       if (level.finish_dig_collect &&
13183           (player->is_digging || player->is_collecting))
13184       {
13185         int last_element = player->last_removed_element;
13186         int move_direction = player->MovDir;
13187         int enter_side = MV_DIR_OPPOSITE(move_direction);
13188         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13189                             CE_PLAYER_COLLECTS_X);
13190
13191         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13192                                             player->index_bit, enter_side);
13193
13194         player->last_removed_element = EL_UNDEFINED;
13195       }
13196
13197       if (!player->active)
13198         RemovePlayer(player);
13199     }
13200
13201     if (level.use_step_counter)
13202     {
13203       int i;
13204
13205       TimePlayed++;
13206
13207       if (TimeLeft > 0)
13208       {
13209         TimeLeft--;
13210
13211         if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
13212           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13213
13214         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13215
13216         DisplayGameControlValues();
13217
13218         if (!TimeLeft && setup.time_limit && !game.LevelSolved)
13219           for (i = 0; i < MAX_PLAYERS; i++)
13220             KillPlayer(&stored_player[i]);
13221       }
13222       else if (game.no_time_limit && !game.all_players_gone)
13223       {
13224         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13225
13226         DisplayGameControlValues();
13227       }
13228     }
13229
13230     if (tape.single_step && tape.recording && !tape.pausing &&
13231         !player->programmed_action)
13232       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13233
13234     if (!player->programmed_action)
13235       CheckSaveEngineSnapshot(player);
13236   }
13237 }
13238
13239 void ScrollScreen(struct PlayerInfo *player, int mode)
13240 {
13241   static unsigned int screen_frame_counter = 0;
13242
13243   if (mode == SCROLL_INIT)
13244   {
13245     // set scrolling step size according to actual player's moving speed
13246     ScrollStepSize = TILEX / player->move_delay_value;
13247
13248     screen_frame_counter = FrameCounter;
13249     ScreenMovDir = player->MovDir;
13250     ScreenMovPos = player->MovPos;
13251     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13252     return;
13253   }
13254   else if (!FrameReached(&screen_frame_counter, 1))
13255     return;
13256
13257   if (ScreenMovPos)
13258   {
13259     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13260     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13261     redraw_mask |= REDRAW_FIELD;
13262   }
13263   else
13264     ScreenMovDir = MV_NONE;
13265 }
13266
13267 void TestIfPlayerTouchesCustomElement(int x, int y)
13268 {
13269   static int xy[4][2] =
13270   {
13271     { 0, -1 },
13272     { -1, 0 },
13273     { +1, 0 },
13274     { 0, +1 }
13275   };
13276   static int trigger_sides[4][2] =
13277   {
13278     // center side       border side
13279     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13280     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13281     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13282     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13283   };
13284   static int touch_dir[4] =
13285   {
13286     MV_LEFT | MV_RIGHT,
13287     MV_UP   | MV_DOWN,
13288     MV_UP   | MV_DOWN,
13289     MV_LEFT | MV_RIGHT
13290   };
13291   int center_element = Tile[x][y];      // should always be non-moving!
13292   int i;
13293
13294   for (i = 0; i < NUM_DIRECTIONS; i++)
13295   {
13296     int xx = x + xy[i][0];
13297     int yy = y + xy[i][1];
13298     int center_side = trigger_sides[i][0];
13299     int border_side = trigger_sides[i][1];
13300     int border_element;
13301
13302     if (!IN_LEV_FIELD(xx, yy))
13303       continue;
13304
13305     if (IS_PLAYER(x, y))                // player found at center element
13306     {
13307       struct PlayerInfo *player = PLAYERINFO(x, y);
13308
13309       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13310         border_element = Tile[xx][yy];          // may be moving!
13311       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13312         border_element = Tile[xx][yy];
13313       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13314         border_element = MovingOrBlocked2Element(xx, yy);
13315       else
13316         continue;               // center and border element do not touch
13317
13318       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13319                                  player->index_bit, border_side);
13320       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13321                                           CE_PLAYER_TOUCHES_X,
13322                                           player->index_bit, border_side);
13323
13324       {
13325         /* use player element that is initially defined in the level playfield,
13326            not the player element that corresponds to the runtime player number
13327            (example: a level that contains EL_PLAYER_3 as the only player would
13328            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13329         int player_element = PLAYERINFO(x, y)->initial_element;
13330
13331         CheckElementChangeBySide(xx, yy, border_element, player_element,
13332                                  CE_TOUCHING_X, border_side);
13333       }
13334     }
13335     else if (IS_PLAYER(xx, yy))         // player found at border element
13336     {
13337       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13338
13339       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13340       {
13341         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13342           continue;             // center and border element do not touch
13343       }
13344
13345       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13346                                  player->index_bit, center_side);
13347       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13348                                           CE_PLAYER_TOUCHES_X,
13349                                           player->index_bit, center_side);
13350
13351       {
13352         /* use player element that is initially defined in the level playfield,
13353            not the player element that corresponds to the runtime player number
13354            (example: a level that contains EL_PLAYER_3 as the only player would
13355            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13356         int player_element = PLAYERINFO(xx, yy)->initial_element;
13357
13358         CheckElementChangeBySide(x, y, center_element, player_element,
13359                                  CE_TOUCHING_X, center_side);
13360       }
13361
13362       break;
13363     }
13364   }
13365 }
13366
13367 void TestIfElementTouchesCustomElement(int x, int y)
13368 {
13369   static int xy[4][2] =
13370   {
13371     { 0, -1 },
13372     { -1, 0 },
13373     { +1, 0 },
13374     { 0, +1 }
13375   };
13376   static int trigger_sides[4][2] =
13377   {
13378     // center side      border side
13379     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13380     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13381     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13382     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13383   };
13384   static int touch_dir[4] =
13385   {
13386     MV_LEFT | MV_RIGHT,
13387     MV_UP   | MV_DOWN,
13388     MV_UP   | MV_DOWN,
13389     MV_LEFT | MV_RIGHT
13390   };
13391   boolean change_center_element = FALSE;
13392   int center_element = Tile[x][y];      // should always be non-moving!
13393   int border_element_old[NUM_DIRECTIONS];
13394   int i;
13395
13396   for (i = 0; i < NUM_DIRECTIONS; i++)
13397   {
13398     int xx = x + xy[i][0];
13399     int yy = y + xy[i][1];
13400     int border_element;
13401
13402     border_element_old[i] = -1;
13403
13404     if (!IN_LEV_FIELD(xx, yy))
13405       continue;
13406
13407     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13408       border_element = Tile[xx][yy];    // may be moving!
13409     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13410       border_element = Tile[xx][yy];
13411     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13412       border_element = MovingOrBlocked2Element(xx, yy);
13413     else
13414       continue;                 // center and border element do not touch
13415
13416     border_element_old[i] = border_element;
13417   }
13418
13419   for (i = 0; i < NUM_DIRECTIONS; i++)
13420   {
13421     int xx = x + xy[i][0];
13422     int yy = y + xy[i][1];
13423     int center_side = trigger_sides[i][0];
13424     int border_element = border_element_old[i];
13425
13426     if (border_element == -1)
13427       continue;
13428
13429     // check for change of border element
13430     CheckElementChangeBySide(xx, yy, border_element, center_element,
13431                              CE_TOUCHING_X, center_side);
13432
13433     // (center element cannot be player, so we dont have to check this here)
13434   }
13435
13436   for (i = 0; i < NUM_DIRECTIONS; i++)
13437   {
13438     int xx = x + xy[i][0];
13439     int yy = y + xy[i][1];
13440     int border_side = trigger_sides[i][1];
13441     int border_element = border_element_old[i];
13442
13443     if (border_element == -1)
13444       continue;
13445
13446     // check for change of center element (but change it only once)
13447     if (!change_center_element)
13448       change_center_element =
13449         CheckElementChangeBySide(x, y, center_element, border_element,
13450                                  CE_TOUCHING_X, border_side);
13451
13452     if (IS_PLAYER(xx, yy))
13453     {
13454       /* use player element that is initially defined in the level playfield,
13455          not the player element that corresponds to the runtime player number
13456          (example: a level that contains EL_PLAYER_3 as the only player would
13457          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13458       int player_element = PLAYERINFO(xx, yy)->initial_element;
13459
13460       CheckElementChangeBySide(x, y, center_element, player_element,
13461                                CE_TOUCHING_X, border_side);
13462     }
13463   }
13464 }
13465
13466 void TestIfElementHitsCustomElement(int x, int y, int direction)
13467 {
13468   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13469   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13470   int hitx = x + dx, hity = y + dy;
13471   int hitting_element = Tile[x][y];
13472   int touched_element;
13473
13474   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13475     return;
13476
13477   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13478                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13479
13480   if (IN_LEV_FIELD(hitx, hity))
13481   {
13482     int opposite_direction = MV_DIR_OPPOSITE(direction);
13483     int hitting_side = direction;
13484     int touched_side = opposite_direction;
13485     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13486                           MovDir[hitx][hity] != direction ||
13487                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13488
13489     object_hit = TRUE;
13490
13491     if (object_hit)
13492     {
13493       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13494                                CE_HITTING_X, touched_side);
13495
13496       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13497                                CE_HIT_BY_X, hitting_side);
13498
13499       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13500                                CE_HIT_BY_SOMETHING, opposite_direction);
13501
13502       if (IS_PLAYER(hitx, hity))
13503       {
13504         /* use player element that is initially defined in the level playfield,
13505            not the player element that corresponds to the runtime player number
13506            (example: a level that contains EL_PLAYER_3 as the only player would
13507            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13508         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13509
13510         CheckElementChangeBySide(x, y, hitting_element, player_element,
13511                                  CE_HITTING_X, touched_side);
13512       }
13513     }
13514   }
13515
13516   // "hitting something" is also true when hitting the playfield border
13517   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13518                            CE_HITTING_SOMETHING, direction);
13519 }
13520
13521 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13522 {
13523   int i, kill_x = -1, kill_y = -1;
13524
13525   int bad_element = -1;
13526   static int test_xy[4][2] =
13527   {
13528     { 0, -1 },
13529     { -1, 0 },
13530     { +1, 0 },
13531     { 0, +1 }
13532   };
13533   static int test_dir[4] =
13534   {
13535     MV_UP,
13536     MV_LEFT,
13537     MV_RIGHT,
13538     MV_DOWN
13539   };
13540
13541   for (i = 0; i < NUM_DIRECTIONS; i++)
13542   {
13543     int test_x, test_y, test_move_dir, test_element;
13544
13545     test_x = good_x + test_xy[i][0];
13546     test_y = good_y + test_xy[i][1];
13547
13548     if (!IN_LEV_FIELD(test_x, test_y))
13549       continue;
13550
13551     test_move_dir =
13552       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13553
13554     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13555
13556     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13557        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13558     */
13559     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13560         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13561     {
13562       kill_x = test_x;
13563       kill_y = test_y;
13564       bad_element = test_element;
13565
13566       break;
13567     }
13568   }
13569
13570   if (kill_x != -1 || kill_y != -1)
13571   {
13572     if (IS_PLAYER(good_x, good_y))
13573     {
13574       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13575
13576       if (player->shield_deadly_time_left > 0 &&
13577           !IS_INDESTRUCTIBLE(bad_element))
13578         Bang(kill_x, kill_y);
13579       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13580         KillPlayer(player);
13581     }
13582     else
13583       Bang(good_x, good_y);
13584   }
13585 }
13586
13587 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13588 {
13589   int i, kill_x = -1, kill_y = -1;
13590   int bad_element = Tile[bad_x][bad_y];
13591   static int test_xy[4][2] =
13592   {
13593     { 0, -1 },
13594     { -1, 0 },
13595     { +1, 0 },
13596     { 0, +1 }
13597   };
13598   static int touch_dir[4] =
13599   {
13600     MV_LEFT | MV_RIGHT,
13601     MV_UP   | MV_DOWN,
13602     MV_UP   | MV_DOWN,
13603     MV_LEFT | MV_RIGHT
13604   };
13605   static int test_dir[4] =
13606   {
13607     MV_UP,
13608     MV_LEFT,
13609     MV_RIGHT,
13610     MV_DOWN
13611   };
13612
13613   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13614     return;
13615
13616   for (i = 0; i < NUM_DIRECTIONS; i++)
13617   {
13618     int test_x, test_y, test_move_dir, test_element;
13619
13620     test_x = bad_x + test_xy[i][0];
13621     test_y = bad_y + test_xy[i][1];
13622
13623     if (!IN_LEV_FIELD(test_x, test_y))
13624       continue;
13625
13626     test_move_dir =
13627       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13628
13629     test_element = Tile[test_x][test_y];
13630
13631     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13632        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13633     */
13634     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13635         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13636     {
13637       // good thing is player or penguin that does not move away
13638       if (IS_PLAYER(test_x, test_y))
13639       {
13640         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13641
13642         if (bad_element == EL_ROBOT && player->is_moving)
13643           continue;     // robot does not kill player if he is moving
13644
13645         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13646         {
13647           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13648             continue;           // center and border element do not touch
13649         }
13650
13651         kill_x = test_x;
13652         kill_y = test_y;
13653
13654         break;
13655       }
13656       else if (test_element == EL_PENGUIN)
13657       {
13658         kill_x = test_x;
13659         kill_y = test_y;
13660
13661         break;
13662       }
13663     }
13664   }
13665
13666   if (kill_x != -1 || kill_y != -1)
13667   {
13668     if (IS_PLAYER(kill_x, kill_y))
13669     {
13670       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13671
13672       if (player->shield_deadly_time_left > 0 &&
13673           !IS_INDESTRUCTIBLE(bad_element))
13674         Bang(bad_x, bad_y);
13675       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13676         KillPlayer(player);
13677     }
13678     else
13679       Bang(kill_x, kill_y);
13680   }
13681 }
13682
13683 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13684 {
13685   int bad_element = Tile[bad_x][bad_y];
13686   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13687   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13688   int test_x = bad_x + dx, test_y = bad_y + dy;
13689   int test_move_dir, test_element;
13690   int kill_x = -1, kill_y = -1;
13691
13692   if (!IN_LEV_FIELD(test_x, test_y))
13693     return;
13694
13695   test_move_dir =
13696     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13697
13698   test_element = Tile[test_x][test_y];
13699
13700   if (test_move_dir != bad_move_dir)
13701   {
13702     // good thing can be player or penguin that does not move away
13703     if (IS_PLAYER(test_x, test_y))
13704     {
13705       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13706
13707       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13708          player as being hit when he is moving towards the bad thing, because
13709          the "get hit by" condition would be lost after the player stops) */
13710       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13711         return;         // player moves away from bad thing
13712
13713       kill_x = test_x;
13714       kill_y = test_y;
13715     }
13716     else if (test_element == EL_PENGUIN)
13717     {
13718       kill_x = test_x;
13719       kill_y = test_y;
13720     }
13721   }
13722
13723   if (kill_x != -1 || kill_y != -1)
13724   {
13725     if (IS_PLAYER(kill_x, kill_y))
13726     {
13727       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13728
13729       if (player->shield_deadly_time_left > 0 &&
13730           !IS_INDESTRUCTIBLE(bad_element))
13731         Bang(bad_x, bad_y);
13732       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13733         KillPlayer(player);
13734     }
13735     else
13736       Bang(kill_x, kill_y);
13737   }
13738 }
13739
13740 void TestIfPlayerTouchesBadThing(int x, int y)
13741 {
13742   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13743 }
13744
13745 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13746 {
13747   TestIfGoodThingHitsBadThing(x, y, move_dir);
13748 }
13749
13750 void TestIfBadThingTouchesPlayer(int x, int y)
13751 {
13752   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13753 }
13754
13755 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13756 {
13757   TestIfBadThingHitsGoodThing(x, y, move_dir);
13758 }
13759
13760 void TestIfFriendTouchesBadThing(int x, int y)
13761 {
13762   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13763 }
13764
13765 void TestIfBadThingTouchesFriend(int x, int y)
13766 {
13767   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13768 }
13769
13770 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13771 {
13772   int i, kill_x = bad_x, kill_y = bad_y;
13773   static int xy[4][2] =
13774   {
13775     { 0, -1 },
13776     { -1, 0 },
13777     { +1, 0 },
13778     { 0, +1 }
13779   };
13780
13781   for (i = 0; i < NUM_DIRECTIONS; i++)
13782   {
13783     int x, y, element;
13784
13785     x = bad_x + xy[i][0];
13786     y = bad_y + xy[i][1];
13787     if (!IN_LEV_FIELD(x, y))
13788       continue;
13789
13790     element = Tile[x][y];
13791     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13792         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13793     {
13794       kill_x = x;
13795       kill_y = y;
13796       break;
13797     }
13798   }
13799
13800   if (kill_x != bad_x || kill_y != bad_y)
13801     Bang(bad_x, bad_y);
13802 }
13803
13804 void KillPlayer(struct PlayerInfo *player)
13805 {
13806   int jx = player->jx, jy = player->jy;
13807
13808   if (!player->active)
13809     return;
13810
13811 #if 0
13812   Debug("game:playing:KillPlayer",
13813         "0: killed == %d, active == %d, reanimated == %d",
13814         player->killed, player->active, player->reanimated);
13815 #endif
13816
13817   /* the following code was introduced to prevent an infinite loop when calling
13818      -> Bang()
13819      -> CheckTriggeredElementChangeExt()
13820      -> ExecuteCustomElementAction()
13821      -> KillPlayer()
13822      -> (infinitely repeating the above sequence of function calls)
13823      which occurs when killing the player while having a CE with the setting
13824      "kill player X when explosion of <player X>"; the solution using a new
13825      field "player->killed" was chosen for backwards compatibility, although
13826      clever use of the fields "player->active" etc. would probably also work */
13827 #if 1
13828   if (player->killed)
13829     return;
13830 #endif
13831
13832   player->killed = TRUE;
13833
13834   // remove accessible field at the player's position
13835   Tile[jx][jy] = EL_EMPTY;
13836
13837   // deactivate shield (else Bang()/Explode() would not work right)
13838   player->shield_normal_time_left = 0;
13839   player->shield_deadly_time_left = 0;
13840
13841 #if 0
13842   Debug("game:playing:KillPlayer",
13843         "1: killed == %d, active == %d, reanimated == %d",
13844         player->killed, player->active, player->reanimated);
13845 #endif
13846
13847   Bang(jx, jy);
13848
13849 #if 0
13850   Debug("game:playing:KillPlayer",
13851         "2: killed == %d, active == %d, reanimated == %d",
13852         player->killed, player->active, player->reanimated);
13853 #endif
13854
13855   if (player->reanimated)       // killed player may have been reanimated
13856     player->killed = player->reanimated = FALSE;
13857   else
13858     BuryPlayer(player);
13859 }
13860
13861 static void KillPlayerUnlessEnemyProtected(int x, int y)
13862 {
13863   if (!PLAYER_ENEMY_PROTECTED(x, y))
13864     KillPlayer(PLAYERINFO(x, y));
13865 }
13866
13867 static void KillPlayerUnlessExplosionProtected(int x, int y)
13868 {
13869   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13870     KillPlayer(PLAYERINFO(x, y));
13871 }
13872
13873 void BuryPlayer(struct PlayerInfo *player)
13874 {
13875   int jx = player->jx, jy = player->jy;
13876
13877   if (!player->active)
13878     return;
13879
13880   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13881   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13882
13883   RemovePlayer(player);
13884
13885   player->buried = TRUE;
13886
13887   if (game.all_players_gone)
13888     game.GameOver = TRUE;
13889 }
13890
13891 void RemovePlayer(struct PlayerInfo *player)
13892 {
13893   int jx = player->jx, jy = player->jy;
13894   int i, found = FALSE;
13895
13896   player->present = FALSE;
13897   player->active = FALSE;
13898
13899   // required for some CE actions (even if the player is not active anymore)
13900   player->MovPos = 0;
13901
13902   if (!ExplodeField[jx][jy])
13903     StorePlayer[jx][jy] = 0;
13904
13905   if (player->is_moving)
13906     TEST_DrawLevelField(player->last_jx, player->last_jy);
13907
13908   for (i = 0; i < MAX_PLAYERS; i++)
13909     if (stored_player[i].active)
13910       found = TRUE;
13911
13912   if (!found)
13913   {
13914     game.all_players_gone = TRUE;
13915     game.GameOver = TRUE;
13916   }
13917
13918   game.exit_x = game.robot_wheel_x = jx;
13919   game.exit_y = game.robot_wheel_y = jy;
13920 }
13921
13922 void ExitPlayer(struct PlayerInfo *player)
13923 {
13924   DrawPlayer(player);   // needed here only to cleanup last field
13925   RemovePlayer(player);
13926
13927   if (game.players_still_needed > 0)
13928     game.players_still_needed--;
13929 }
13930
13931 static void SetFieldForSnapping(int x, int y, int element, int direction,
13932                                 int player_index_bit)
13933 {
13934   struct ElementInfo *ei = &element_info[element];
13935   int direction_bit = MV_DIR_TO_BIT(direction);
13936   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13937   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13938                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13939
13940   Tile[x][y] = EL_ELEMENT_SNAPPING;
13941   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13942   MovDir[x][y] = direction;
13943   Store[x][y] = element;
13944   Store2[x][y] = player_index_bit;
13945
13946   ResetGfxAnimation(x, y);
13947
13948   GfxElement[x][y] = element;
13949   GfxAction[x][y] = action;
13950   GfxDir[x][y] = direction;
13951   GfxFrame[x][y] = -1;
13952 }
13953
13954 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13955                                    int player_index_bit)
13956 {
13957   TestIfElementTouchesCustomElement(x, y);      // for empty space
13958
13959   if (level.finish_dig_collect)
13960   {
13961     int dig_side = MV_DIR_OPPOSITE(direction);
13962
13963     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13964                                         player_index_bit, dig_side);
13965   }
13966 }
13967
13968 /*
13969   =============================================================================
13970   checkDiagonalPushing()
13971   -----------------------------------------------------------------------------
13972   check if diagonal input device direction results in pushing of object
13973   (by checking if the alternative direction is walkable, diggable, ...)
13974   =============================================================================
13975 */
13976
13977 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13978                                     int x, int y, int real_dx, int real_dy)
13979 {
13980   int jx, jy, dx, dy, xx, yy;
13981
13982   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13983     return TRUE;
13984
13985   // diagonal direction: check alternative direction
13986   jx = player->jx;
13987   jy = player->jy;
13988   dx = x - jx;
13989   dy = y - jy;
13990   xx = jx + (dx == 0 ? real_dx : 0);
13991   yy = jy + (dy == 0 ? real_dy : 0);
13992
13993   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13994 }
13995
13996 /*
13997   =============================================================================
13998   DigField()
13999   -----------------------------------------------------------------------------
14000   x, y:                 field next to player (non-diagonal) to try to dig to
14001   real_dx, real_dy:     direction as read from input device (can be diagonal)
14002   =============================================================================
14003 */
14004
14005 static int DigField(struct PlayerInfo *player,
14006                     int oldx, int oldy, int x, int y,
14007                     int real_dx, int real_dy, int mode)
14008 {
14009   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14010   boolean player_was_pushing = player->is_pushing;
14011   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14012   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14013   int jx = oldx, jy = oldy;
14014   int dx = x - jx, dy = y - jy;
14015   int nextx = x + dx, nexty = y + dy;
14016   int move_direction = (dx == -1 ? MV_LEFT  :
14017                         dx == +1 ? MV_RIGHT :
14018                         dy == -1 ? MV_UP    :
14019                         dy == +1 ? MV_DOWN  : MV_NONE);
14020   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14021   int dig_side = MV_DIR_OPPOSITE(move_direction);
14022   int old_element = Tile[jx][jy];
14023   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14024   int collect_count;
14025
14026   if (is_player)                // function can also be called by EL_PENGUIN
14027   {
14028     if (player->MovPos == 0)
14029     {
14030       player->is_digging = FALSE;
14031       player->is_collecting = FALSE;
14032     }
14033
14034     if (player->MovPos == 0)    // last pushing move finished
14035       player->is_pushing = FALSE;
14036
14037     if (mode == DF_NO_PUSH)     // player just stopped pushing
14038     {
14039       player->is_switching = FALSE;
14040       player->push_delay = -1;
14041
14042       return MP_NO_ACTION;
14043     }
14044   }
14045
14046   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14047     old_element = Back[jx][jy];
14048
14049   // in case of element dropped at player position, check background
14050   else if (Back[jx][jy] != EL_EMPTY &&
14051            game.engine_version >= VERSION_IDENT(2,2,0,0))
14052     old_element = Back[jx][jy];
14053
14054   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14055     return MP_NO_ACTION;        // field has no opening in this direction
14056
14057   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14058     return MP_NO_ACTION;        // field has no opening in this direction
14059
14060   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14061   {
14062     SplashAcid(x, y);
14063
14064     Tile[jx][jy] = player->artwork_element;
14065     InitMovingField(jx, jy, MV_DOWN);
14066     Store[jx][jy] = EL_ACID;
14067     ContinueMoving(jx, jy);
14068     BuryPlayer(player);
14069
14070     return MP_DONT_RUN_INTO;
14071   }
14072
14073   if (player_can_move && DONT_RUN_INTO(element))
14074   {
14075     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14076
14077     return MP_DONT_RUN_INTO;
14078   }
14079
14080   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14081     return MP_NO_ACTION;
14082
14083   collect_count = element_info[element].collect_count_initial;
14084
14085   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14086     return MP_NO_ACTION;
14087
14088   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14089     player_can_move = player_can_move_or_snap;
14090
14091   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14092       game.engine_version >= VERSION_IDENT(2,2,0,0))
14093   {
14094     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14095                                player->index_bit, dig_side);
14096     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14097                                         player->index_bit, dig_side);
14098
14099     if (element == EL_DC_LANDMINE)
14100       Bang(x, y);
14101
14102     if (Tile[x][y] != element)          // field changed by snapping
14103       return MP_ACTION;
14104
14105     return MP_NO_ACTION;
14106   }
14107
14108   if (player->gravity && is_player && !player->is_auto_moving &&
14109       canFallDown(player) && move_direction != MV_DOWN &&
14110       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14111     return MP_NO_ACTION;        // player cannot walk here due to gravity
14112
14113   if (player_can_move &&
14114       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14115   {
14116     int sound_element = SND_ELEMENT(element);
14117     int sound_action = ACTION_WALKING;
14118
14119     if (IS_RND_GATE(element))
14120     {
14121       if (!player->key[RND_GATE_NR(element)])
14122         return MP_NO_ACTION;
14123     }
14124     else if (IS_RND_GATE_GRAY(element))
14125     {
14126       if (!player->key[RND_GATE_GRAY_NR(element)])
14127         return MP_NO_ACTION;
14128     }
14129     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14130     {
14131       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14132         return MP_NO_ACTION;
14133     }
14134     else if (element == EL_EXIT_OPEN ||
14135              element == EL_EM_EXIT_OPEN ||
14136              element == EL_EM_EXIT_OPENING ||
14137              element == EL_STEEL_EXIT_OPEN ||
14138              element == EL_EM_STEEL_EXIT_OPEN ||
14139              element == EL_EM_STEEL_EXIT_OPENING ||
14140              element == EL_SP_EXIT_OPEN ||
14141              element == EL_SP_EXIT_OPENING)
14142     {
14143       sound_action = ACTION_PASSING;    // player is passing exit
14144     }
14145     else if (element == EL_EMPTY)
14146     {
14147       sound_action = ACTION_MOVING;             // nothing to walk on
14148     }
14149
14150     // play sound from background or player, whatever is available
14151     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14152       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14153     else
14154       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14155   }
14156   else if (player_can_move &&
14157            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14158   {
14159     if (!ACCESS_FROM(element, opposite_direction))
14160       return MP_NO_ACTION;      // field not accessible from this direction
14161
14162     if (CAN_MOVE(element))      // only fixed elements can be passed!
14163       return MP_NO_ACTION;
14164
14165     if (IS_EM_GATE(element))
14166     {
14167       if (!player->key[EM_GATE_NR(element)])
14168         return MP_NO_ACTION;
14169     }
14170     else if (IS_EM_GATE_GRAY(element))
14171     {
14172       if (!player->key[EM_GATE_GRAY_NR(element)])
14173         return MP_NO_ACTION;
14174     }
14175     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14176     {
14177       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14178         return MP_NO_ACTION;
14179     }
14180     else if (IS_EMC_GATE(element))
14181     {
14182       if (!player->key[EMC_GATE_NR(element)])
14183         return MP_NO_ACTION;
14184     }
14185     else if (IS_EMC_GATE_GRAY(element))
14186     {
14187       if (!player->key[EMC_GATE_GRAY_NR(element)])
14188         return MP_NO_ACTION;
14189     }
14190     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14191     {
14192       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14193         return MP_NO_ACTION;
14194     }
14195     else if (element == EL_DC_GATE_WHITE ||
14196              element == EL_DC_GATE_WHITE_GRAY ||
14197              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14198     {
14199       if (player->num_white_keys == 0)
14200         return MP_NO_ACTION;
14201
14202       player->num_white_keys--;
14203     }
14204     else if (IS_SP_PORT(element))
14205     {
14206       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14207           element == EL_SP_GRAVITY_PORT_RIGHT ||
14208           element == EL_SP_GRAVITY_PORT_UP ||
14209           element == EL_SP_GRAVITY_PORT_DOWN)
14210         player->gravity = !player->gravity;
14211       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14212                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14213                element == EL_SP_GRAVITY_ON_PORT_UP ||
14214                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14215         player->gravity = TRUE;
14216       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14217                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14218                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14219                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14220         player->gravity = FALSE;
14221     }
14222
14223     // automatically move to the next field with double speed
14224     player->programmed_action = move_direction;
14225
14226     if (player->move_delay_reset_counter == 0)
14227     {
14228       player->move_delay_reset_counter = 2;     // two double speed steps
14229
14230       DOUBLE_PLAYER_SPEED(player);
14231     }
14232
14233     PlayLevelSoundAction(x, y, ACTION_PASSING);
14234   }
14235   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14236   {
14237     RemoveField(x, y);
14238
14239     if (mode != DF_SNAP)
14240     {
14241       GfxElement[x][y] = GFX_ELEMENT(element);
14242       player->is_digging = TRUE;
14243     }
14244
14245     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14246
14247     // use old behaviour for old levels (digging)
14248     if (!level.finish_dig_collect)
14249     {
14250       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14251                                           player->index_bit, dig_side);
14252
14253       // if digging triggered player relocation, finish digging tile
14254       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14255         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14256     }
14257
14258     if (mode == DF_SNAP)
14259     {
14260       if (level.block_snap_field)
14261         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14262       else
14263         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14264
14265       // use old behaviour for old levels (snapping)
14266       if (!level.finish_dig_collect)
14267         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14268                                             player->index_bit, dig_side);
14269     }
14270   }
14271   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14272   {
14273     RemoveField(x, y);
14274
14275     if (is_player && mode != DF_SNAP)
14276     {
14277       GfxElement[x][y] = element;
14278       player->is_collecting = TRUE;
14279     }
14280
14281     if (element == EL_SPEED_PILL)
14282     {
14283       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14284     }
14285     else if (element == EL_EXTRA_TIME && level.time > 0)
14286     {
14287       TimeLeft += level.extra_time;
14288
14289       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14290
14291       DisplayGameControlValues();
14292     }
14293     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14294     {
14295       player->shield_normal_time_left += level.shield_normal_time;
14296       if (element == EL_SHIELD_DEADLY)
14297         player->shield_deadly_time_left += level.shield_deadly_time;
14298     }
14299     else if (element == EL_DYNAMITE ||
14300              element == EL_EM_DYNAMITE ||
14301              element == EL_SP_DISK_RED)
14302     {
14303       if (player->inventory_size < MAX_INVENTORY_SIZE)
14304         player->inventory_element[player->inventory_size++] = element;
14305
14306       DrawGameDoorValues();
14307     }
14308     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14309     {
14310       player->dynabomb_count++;
14311       player->dynabombs_left++;
14312     }
14313     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14314     {
14315       player->dynabomb_size++;
14316     }
14317     else if (element == EL_DYNABOMB_INCREASE_POWER)
14318     {
14319       player->dynabomb_xl = TRUE;
14320     }
14321     else if (IS_KEY(element))
14322     {
14323       player->key[KEY_NR(element)] = TRUE;
14324
14325       DrawGameDoorValues();
14326     }
14327     else if (element == EL_DC_KEY_WHITE)
14328     {
14329       player->num_white_keys++;
14330
14331       // display white keys?
14332       // DrawGameDoorValues();
14333     }
14334     else if (IS_ENVELOPE(element))
14335     {
14336       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14337
14338       if (!wait_for_snapping)
14339         player->show_envelope = element;
14340     }
14341     else if (element == EL_EMC_LENSES)
14342     {
14343       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14344
14345       RedrawAllInvisibleElementsForLenses();
14346     }
14347     else if (element == EL_EMC_MAGNIFIER)
14348     {
14349       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14350
14351       RedrawAllInvisibleElementsForMagnifier();
14352     }
14353     else if (IS_DROPPABLE(element) ||
14354              IS_THROWABLE(element))     // can be collected and dropped
14355     {
14356       int i;
14357
14358       if (collect_count == 0)
14359         player->inventory_infinite_element = element;
14360       else
14361         for (i = 0; i < collect_count; i++)
14362           if (player->inventory_size < MAX_INVENTORY_SIZE)
14363             player->inventory_element[player->inventory_size++] = element;
14364
14365       DrawGameDoorValues();
14366     }
14367     else if (collect_count > 0)
14368     {
14369       game.gems_still_needed -= collect_count;
14370       if (game.gems_still_needed < 0)
14371         game.gems_still_needed = 0;
14372
14373       game.snapshot.collected_item = TRUE;
14374
14375       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14376
14377       DisplayGameControlValues();
14378     }
14379
14380     RaiseScoreElement(element);
14381     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14382
14383     // use old behaviour for old levels (collecting)
14384     if (!level.finish_dig_collect && is_player)
14385     {
14386       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14387                                           player->index_bit, dig_side);
14388
14389       // if collecting triggered player relocation, finish collecting tile
14390       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14391         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14392     }
14393
14394     if (mode == DF_SNAP)
14395     {
14396       if (level.block_snap_field)
14397         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14398       else
14399         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14400
14401       // use old behaviour for old levels (snapping)
14402       if (!level.finish_dig_collect)
14403         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14404                                             player->index_bit, dig_side);
14405     }
14406   }
14407   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14408   {
14409     if (mode == DF_SNAP && element != EL_BD_ROCK)
14410       return MP_NO_ACTION;
14411
14412     if (CAN_FALL(element) && dy)
14413       return MP_NO_ACTION;
14414
14415     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14416         !(element == EL_SPRING && level.use_spring_bug))
14417       return MP_NO_ACTION;
14418
14419     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14420         ((move_direction & MV_VERTICAL &&
14421           ((element_info[element].move_pattern & MV_LEFT &&
14422             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14423            (element_info[element].move_pattern & MV_RIGHT &&
14424             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14425          (move_direction & MV_HORIZONTAL &&
14426           ((element_info[element].move_pattern & MV_UP &&
14427             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14428            (element_info[element].move_pattern & MV_DOWN &&
14429             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14430       return MP_NO_ACTION;
14431
14432     // do not push elements already moving away faster than player
14433     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14434         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14435       return MP_NO_ACTION;
14436
14437     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14438     {
14439       if (player->push_delay_value == -1 || !player_was_pushing)
14440         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14441     }
14442     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14443     {
14444       if (player->push_delay_value == -1)
14445         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14446     }
14447     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14448     {
14449       if (!player->is_pushing)
14450         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14451     }
14452
14453     player->is_pushing = TRUE;
14454     player->is_active = TRUE;
14455
14456     if (!(IN_LEV_FIELD(nextx, nexty) &&
14457           (IS_FREE(nextx, nexty) ||
14458            (IS_SB_ELEMENT(element) &&
14459             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14460            (IS_CUSTOM_ELEMENT(element) &&
14461             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14462       return MP_NO_ACTION;
14463
14464     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14465       return MP_NO_ACTION;
14466
14467     if (player->push_delay == -1)       // new pushing; restart delay
14468       player->push_delay = 0;
14469
14470     if (player->push_delay < player->push_delay_value &&
14471         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14472         element != EL_SPRING && element != EL_BALLOON)
14473     {
14474       // make sure that there is no move delay before next try to push
14475       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14476         player->move_delay = 0;
14477
14478       return MP_NO_ACTION;
14479     }
14480
14481     if (IS_CUSTOM_ELEMENT(element) &&
14482         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14483     {
14484       if (!DigFieldByCE(nextx, nexty, element))
14485         return MP_NO_ACTION;
14486     }
14487
14488     if (IS_SB_ELEMENT(element))
14489     {
14490       boolean sokoban_task_solved = FALSE;
14491
14492       if (element == EL_SOKOBAN_FIELD_FULL)
14493       {
14494         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14495
14496         IncrementSokobanFieldsNeeded();
14497         IncrementSokobanObjectsNeeded();
14498       }
14499
14500       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14501       {
14502         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14503
14504         DecrementSokobanFieldsNeeded();
14505         DecrementSokobanObjectsNeeded();
14506
14507         // sokoban object was pushed from empty field to sokoban field
14508         if (Back[x][y] == EL_EMPTY)
14509           sokoban_task_solved = TRUE;
14510       }
14511
14512       Tile[x][y] = EL_SOKOBAN_OBJECT;
14513
14514       if (Back[x][y] == Back[nextx][nexty])
14515         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14516       else if (Back[x][y] != 0)
14517         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14518                                     ACTION_EMPTYING);
14519       else
14520         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14521                                     ACTION_FILLING);
14522
14523       if (sokoban_task_solved &&
14524           game.sokoban_fields_still_needed == 0 &&
14525           game.sokoban_objects_still_needed == 0 &&
14526           level.auto_exit_sokoban)
14527       {
14528         game.players_still_needed = 0;
14529
14530         LevelSolved();
14531
14532         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14533       }
14534     }
14535     else
14536       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14537
14538     InitMovingField(x, y, move_direction);
14539     GfxAction[x][y] = ACTION_PUSHING;
14540
14541     if (mode == DF_SNAP)
14542       ContinueMoving(x, y);
14543     else
14544       MovPos[x][y] = (dx != 0 ? dx : dy);
14545
14546     Pushed[x][y] = TRUE;
14547     Pushed[nextx][nexty] = TRUE;
14548
14549     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14550       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14551     else
14552       player->push_delay_value = -1;    // get new value later
14553
14554     // check for element change _after_ element has been pushed
14555     if (game.use_change_when_pushing_bug)
14556     {
14557       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14558                                  player->index_bit, dig_side);
14559       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14560                                           player->index_bit, dig_side);
14561     }
14562   }
14563   else if (IS_SWITCHABLE(element))
14564   {
14565     if (PLAYER_SWITCHING(player, x, y))
14566     {
14567       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14568                                           player->index_bit, dig_side);
14569
14570       return MP_ACTION;
14571     }
14572
14573     player->is_switching = TRUE;
14574     player->switch_x = x;
14575     player->switch_y = y;
14576
14577     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14578
14579     if (element == EL_ROBOT_WHEEL)
14580     {
14581       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14582
14583       game.robot_wheel_x = x;
14584       game.robot_wheel_y = y;
14585       game.robot_wheel_active = TRUE;
14586
14587       TEST_DrawLevelField(x, y);
14588     }
14589     else if (element == EL_SP_TERMINAL)
14590     {
14591       int xx, yy;
14592
14593       SCAN_PLAYFIELD(xx, yy)
14594       {
14595         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14596         {
14597           Bang(xx, yy);
14598         }
14599         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14600         {
14601           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14602
14603           ResetGfxAnimation(xx, yy);
14604           TEST_DrawLevelField(xx, yy);
14605         }
14606       }
14607     }
14608     else if (IS_BELT_SWITCH(element))
14609     {
14610       ToggleBeltSwitch(x, y);
14611     }
14612     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14613              element == EL_SWITCHGATE_SWITCH_DOWN ||
14614              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14615              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14616     {
14617       ToggleSwitchgateSwitch(x, y);
14618     }
14619     else if (element == EL_LIGHT_SWITCH ||
14620              element == EL_LIGHT_SWITCH_ACTIVE)
14621     {
14622       ToggleLightSwitch(x, y);
14623     }
14624     else if (element == EL_TIMEGATE_SWITCH ||
14625              element == EL_DC_TIMEGATE_SWITCH)
14626     {
14627       ActivateTimegateSwitch(x, y);
14628     }
14629     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14630              element == EL_BALLOON_SWITCH_RIGHT ||
14631              element == EL_BALLOON_SWITCH_UP    ||
14632              element == EL_BALLOON_SWITCH_DOWN  ||
14633              element == EL_BALLOON_SWITCH_NONE  ||
14634              element == EL_BALLOON_SWITCH_ANY)
14635     {
14636       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14637                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14638                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14639                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14640                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14641                              move_direction);
14642     }
14643     else if (element == EL_LAMP)
14644     {
14645       Tile[x][y] = EL_LAMP_ACTIVE;
14646       game.lights_still_needed--;
14647
14648       ResetGfxAnimation(x, y);
14649       TEST_DrawLevelField(x, y);
14650     }
14651     else if (element == EL_TIME_ORB_FULL)
14652     {
14653       Tile[x][y] = EL_TIME_ORB_EMPTY;
14654
14655       if (level.time > 0 || level.use_time_orb_bug)
14656       {
14657         TimeLeft += level.time_orb_time;
14658         game.no_time_limit = FALSE;
14659
14660         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14661
14662         DisplayGameControlValues();
14663       }
14664
14665       ResetGfxAnimation(x, y);
14666       TEST_DrawLevelField(x, y);
14667     }
14668     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14669              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14670     {
14671       int xx, yy;
14672
14673       game.ball_active = !game.ball_active;
14674
14675       SCAN_PLAYFIELD(xx, yy)
14676       {
14677         int e = Tile[xx][yy];
14678
14679         if (game.ball_active)
14680         {
14681           if (e == EL_EMC_MAGIC_BALL)
14682             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14683           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14684             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14685         }
14686         else
14687         {
14688           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14689             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14690           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14691             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14692         }
14693       }
14694     }
14695
14696     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14697                                         player->index_bit, dig_side);
14698
14699     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14700                                         player->index_bit, dig_side);
14701
14702     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14703                                         player->index_bit, dig_side);
14704
14705     return MP_ACTION;
14706   }
14707   else
14708   {
14709     if (!PLAYER_SWITCHING(player, x, y))
14710     {
14711       player->is_switching = TRUE;
14712       player->switch_x = x;
14713       player->switch_y = y;
14714
14715       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14716                                  player->index_bit, dig_side);
14717       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14718                                           player->index_bit, dig_side);
14719
14720       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14721                                  player->index_bit, dig_side);
14722       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14723                                           player->index_bit, dig_side);
14724     }
14725
14726     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14727                                player->index_bit, dig_side);
14728     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14729                                         player->index_bit, dig_side);
14730
14731     return MP_NO_ACTION;
14732   }
14733
14734   player->push_delay = -1;
14735
14736   if (is_player)                // function can also be called by EL_PENGUIN
14737   {
14738     if (Tile[x][y] != element)          // really digged/collected something
14739     {
14740       player->is_collecting = !player->is_digging;
14741       player->is_active = TRUE;
14742
14743       player->last_removed_element = element;
14744     }
14745   }
14746
14747   return MP_MOVING;
14748 }
14749
14750 static boolean DigFieldByCE(int x, int y, int digging_element)
14751 {
14752   int element = Tile[x][y];
14753
14754   if (!IS_FREE(x, y))
14755   {
14756     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14757                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14758                   ACTION_BREAKING);
14759
14760     // no element can dig solid indestructible elements
14761     if (IS_INDESTRUCTIBLE(element) &&
14762         !IS_DIGGABLE(element) &&
14763         !IS_COLLECTIBLE(element))
14764       return FALSE;
14765
14766     if (AmoebaNr[x][y] &&
14767         (element == EL_AMOEBA_FULL ||
14768          element == EL_BD_AMOEBA ||
14769          element == EL_AMOEBA_GROWING))
14770     {
14771       AmoebaCnt[AmoebaNr[x][y]]--;
14772       AmoebaCnt2[AmoebaNr[x][y]]--;
14773     }
14774
14775     if (IS_MOVING(x, y))
14776       RemoveMovingField(x, y);
14777     else
14778     {
14779       RemoveField(x, y);
14780       TEST_DrawLevelField(x, y);
14781     }
14782
14783     // if digged element was about to explode, prevent the explosion
14784     ExplodeField[x][y] = EX_TYPE_NONE;
14785
14786     PlayLevelSoundAction(x, y, action);
14787   }
14788
14789   Store[x][y] = EL_EMPTY;
14790
14791   // this makes it possible to leave the removed element again
14792   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14793     Store[x][y] = element;
14794
14795   return TRUE;
14796 }
14797
14798 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14799 {
14800   int jx = player->jx, jy = player->jy;
14801   int x = jx + dx, y = jy + dy;
14802   int snap_direction = (dx == -1 ? MV_LEFT  :
14803                         dx == +1 ? MV_RIGHT :
14804                         dy == -1 ? MV_UP    :
14805                         dy == +1 ? MV_DOWN  : MV_NONE);
14806   boolean can_continue_snapping = (level.continuous_snapping &&
14807                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14808
14809   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14810     return FALSE;
14811
14812   if (!player->active || !IN_LEV_FIELD(x, y))
14813     return FALSE;
14814
14815   if (dx && dy)
14816     return FALSE;
14817
14818   if (!dx && !dy)
14819   {
14820     if (player->MovPos == 0)
14821       player->is_pushing = FALSE;
14822
14823     player->is_snapping = FALSE;
14824
14825     if (player->MovPos == 0)
14826     {
14827       player->is_moving = FALSE;
14828       player->is_digging = FALSE;
14829       player->is_collecting = FALSE;
14830     }
14831
14832     return FALSE;
14833   }
14834
14835   // prevent snapping with already pressed snap key when not allowed
14836   if (player->is_snapping && !can_continue_snapping)
14837     return FALSE;
14838
14839   player->MovDir = snap_direction;
14840
14841   if (player->MovPos == 0)
14842   {
14843     player->is_moving = FALSE;
14844     player->is_digging = FALSE;
14845     player->is_collecting = FALSE;
14846   }
14847
14848   player->is_dropping = FALSE;
14849   player->is_dropping_pressed = FALSE;
14850   player->drop_pressed_delay = 0;
14851
14852   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14853     return FALSE;
14854
14855   player->is_snapping = TRUE;
14856   player->is_active = TRUE;
14857
14858   if (player->MovPos == 0)
14859   {
14860     player->is_moving = FALSE;
14861     player->is_digging = FALSE;
14862     player->is_collecting = FALSE;
14863   }
14864
14865   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14866     TEST_DrawLevelField(player->last_jx, player->last_jy);
14867
14868   TEST_DrawLevelField(x, y);
14869
14870   return TRUE;
14871 }
14872
14873 static boolean DropElement(struct PlayerInfo *player)
14874 {
14875   int old_element, new_element;
14876   int dropx = player->jx, dropy = player->jy;
14877   int drop_direction = player->MovDir;
14878   int drop_side = drop_direction;
14879   int drop_element = get_next_dropped_element(player);
14880
14881   /* do not drop an element on top of another element; when holding drop key
14882      pressed without moving, dropped element must move away before the next
14883      element can be dropped (this is especially important if the next element
14884      is dynamite, which can be placed on background for historical reasons) */
14885   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14886     return MP_ACTION;
14887
14888   if (IS_THROWABLE(drop_element))
14889   {
14890     dropx += GET_DX_FROM_DIR(drop_direction);
14891     dropy += GET_DY_FROM_DIR(drop_direction);
14892
14893     if (!IN_LEV_FIELD(dropx, dropy))
14894       return FALSE;
14895   }
14896
14897   old_element = Tile[dropx][dropy];     // old element at dropping position
14898   new_element = drop_element;           // default: no change when dropping
14899
14900   // check if player is active, not moving and ready to drop
14901   if (!player->active || player->MovPos || player->drop_delay > 0)
14902     return FALSE;
14903
14904   // check if player has anything that can be dropped
14905   if (new_element == EL_UNDEFINED)
14906     return FALSE;
14907
14908   // only set if player has anything that can be dropped
14909   player->is_dropping_pressed = TRUE;
14910
14911   // check if drop key was pressed long enough for EM style dynamite
14912   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14913     return FALSE;
14914
14915   // check if anything can be dropped at the current position
14916   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14917     return FALSE;
14918
14919   // collected custom elements can only be dropped on empty fields
14920   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14921     return FALSE;
14922
14923   if (old_element != EL_EMPTY)
14924     Back[dropx][dropy] = old_element;   // store old element on this field
14925
14926   ResetGfxAnimation(dropx, dropy);
14927   ResetRandomAnimationValue(dropx, dropy);
14928
14929   if (player->inventory_size > 0 ||
14930       player->inventory_infinite_element != EL_UNDEFINED)
14931   {
14932     if (player->inventory_size > 0)
14933     {
14934       player->inventory_size--;
14935
14936       DrawGameDoorValues();
14937
14938       if (new_element == EL_DYNAMITE)
14939         new_element = EL_DYNAMITE_ACTIVE;
14940       else if (new_element == EL_EM_DYNAMITE)
14941         new_element = EL_EM_DYNAMITE_ACTIVE;
14942       else if (new_element == EL_SP_DISK_RED)
14943         new_element = EL_SP_DISK_RED_ACTIVE;
14944     }
14945
14946     Tile[dropx][dropy] = new_element;
14947
14948     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14949       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14950                           el2img(Tile[dropx][dropy]), 0);
14951
14952     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14953
14954     // needed if previous element just changed to "empty" in the last frame
14955     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14956
14957     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14958                                player->index_bit, drop_side);
14959     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14960                                         CE_PLAYER_DROPS_X,
14961                                         player->index_bit, drop_side);
14962
14963     TestIfElementTouchesCustomElement(dropx, dropy);
14964   }
14965   else          // player is dropping a dyna bomb
14966   {
14967     player->dynabombs_left--;
14968
14969     Tile[dropx][dropy] = new_element;
14970
14971     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14972       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14973                           el2img(Tile[dropx][dropy]), 0);
14974
14975     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14976   }
14977
14978   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14979     InitField_WithBug1(dropx, dropy, FALSE);
14980
14981   new_element = Tile[dropx][dropy];     // element might have changed
14982
14983   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14984       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14985   {
14986     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14987       MovDir[dropx][dropy] = drop_direction;
14988
14989     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14990
14991     // do not cause impact style collision by dropping elements that can fall
14992     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14993   }
14994
14995   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14996   player->is_dropping = TRUE;
14997
14998   player->drop_pressed_delay = 0;
14999   player->is_dropping_pressed = FALSE;
15000
15001   player->drop_x = dropx;
15002   player->drop_y = dropy;
15003
15004   return TRUE;
15005 }
15006
15007 // ----------------------------------------------------------------------------
15008 // game sound playing functions
15009 // ----------------------------------------------------------------------------
15010
15011 static int *loop_sound_frame = NULL;
15012 static int *loop_sound_volume = NULL;
15013
15014 void InitPlayLevelSound(void)
15015 {
15016   int num_sounds = getSoundListSize();
15017
15018   checked_free(loop_sound_frame);
15019   checked_free(loop_sound_volume);
15020
15021   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15022   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15023 }
15024
15025 static void PlayLevelSound(int x, int y, int nr)
15026 {
15027   int sx = SCREENX(x), sy = SCREENY(y);
15028   int volume, stereo_position;
15029   int max_distance = 8;
15030   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15031
15032   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15033       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15034     return;
15035
15036   if (!IN_LEV_FIELD(x, y) ||
15037       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15038       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15039     return;
15040
15041   volume = SOUND_MAX_VOLUME;
15042
15043   if (!IN_SCR_FIELD(sx, sy))
15044   {
15045     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15046     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15047
15048     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15049   }
15050
15051   stereo_position = (SOUND_MAX_LEFT +
15052                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15053                      (SCR_FIELDX + 2 * max_distance));
15054
15055   if (IS_LOOP_SOUND(nr))
15056   {
15057     /* This assures that quieter loop sounds do not overwrite louder ones,
15058        while restarting sound volume comparison with each new game frame. */
15059
15060     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15061       return;
15062
15063     loop_sound_volume[nr] = volume;
15064     loop_sound_frame[nr] = FrameCounter;
15065   }
15066
15067   PlaySoundExt(nr, volume, stereo_position, type);
15068 }
15069
15070 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15071 {
15072   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15073                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15074                  y < LEVELY(BY1) ? LEVELY(BY1) :
15075                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15076                  sound_action);
15077 }
15078
15079 static void PlayLevelSoundAction(int x, int y, int action)
15080 {
15081   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15082 }
15083
15084 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15085 {
15086   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15087
15088   if (sound_effect != SND_UNDEFINED)
15089     PlayLevelSound(x, y, sound_effect);
15090 }
15091
15092 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15093                                               int action)
15094 {
15095   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15096
15097   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15098     PlayLevelSound(x, y, sound_effect);
15099 }
15100
15101 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15102 {
15103   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15104
15105   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15106     PlayLevelSound(x, y, sound_effect);
15107 }
15108
15109 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15110 {
15111   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15112
15113   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15114     StopSound(sound_effect);
15115 }
15116
15117 static int getLevelMusicNr(void)
15118 {
15119   if (levelset.music[level_nr] != MUS_UNDEFINED)
15120     return levelset.music[level_nr];            // from config file
15121   else
15122     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15123 }
15124
15125 static void FadeLevelSounds(void)
15126 {
15127   FadeSounds();
15128 }
15129
15130 static void FadeLevelMusic(void)
15131 {
15132   int music_nr = getLevelMusicNr();
15133   char *curr_music = getCurrentlyPlayingMusicFilename();
15134   char *next_music = getMusicInfoEntryFilename(music_nr);
15135
15136   if (!strEqual(curr_music, next_music))
15137     FadeMusic();
15138 }
15139
15140 void FadeLevelSoundsAndMusic(void)
15141 {
15142   FadeLevelSounds();
15143   FadeLevelMusic();
15144 }
15145
15146 static void PlayLevelMusic(void)
15147 {
15148   int music_nr = getLevelMusicNr();
15149   char *curr_music = getCurrentlyPlayingMusicFilename();
15150   char *next_music = getMusicInfoEntryFilename(music_nr);
15151
15152   if (!strEqual(curr_music, next_music))
15153     PlayMusicLoop(music_nr);
15154 }
15155
15156 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15157 {
15158   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15159   int offset = 0;
15160   int x = xx - offset;
15161   int y = yy - offset;
15162
15163   switch (sample)
15164   {
15165     case SOUND_blank:
15166       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15167       break;
15168
15169     case SOUND_roll:
15170       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15171       break;
15172
15173     case SOUND_stone:
15174       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15175       break;
15176
15177     case SOUND_nut:
15178       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15179       break;
15180
15181     case SOUND_crack:
15182       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15183       break;
15184
15185     case SOUND_bug:
15186       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15187       break;
15188
15189     case SOUND_tank:
15190       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15191       break;
15192
15193     case SOUND_android_clone:
15194       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15195       break;
15196
15197     case SOUND_android_move:
15198       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15199       break;
15200
15201     case SOUND_spring:
15202       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15203       break;
15204
15205     case SOUND_slurp:
15206       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15207       break;
15208
15209     case SOUND_eater:
15210       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15211       break;
15212
15213     case SOUND_eater_eat:
15214       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15215       break;
15216
15217     case SOUND_alien:
15218       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15219       break;
15220
15221     case SOUND_collect:
15222       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15223       break;
15224
15225     case SOUND_diamond:
15226       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15227       break;
15228
15229     case SOUND_squash:
15230       // !!! CHECK THIS !!!
15231 #if 1
15232       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15233 #else
15234       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15235 #endif
15236       break;
15237
15238     case SOUND_wonderfall:
15239       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15240       break;
15241
15242     case SOUND_drip:
15243       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15244       break;
15245
15246     case SOUND_push:
15247       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15248       break;
15249
15250     case SOUND_dirt:
15251       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15252       break;
15253
15254     case SOUND_acid:
15255       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15256       break;
15257
15258     case SOUND_ball:
15259       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15260       break;
15261
15262     case SOUND_slide:
15263       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15264       break;
15265
15266     case SOUND_wonder:
15267       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15268       break;
15269
15270     case SOUND_door:
15271       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15272       break;
15273
15274     case SOUND_exit_open:
15275       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15276       break;
15277
15278     case SOUND_exit_leave:
15279       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15280       break;
15281
15282     case SOUND_dynamite:
15283       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15284       break;
15285
15286     case SOUND_tick:
15287       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15288       break;
15289
15290     case SOUND_press:
15291       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15292       break;
15293
15294     case SOUND_wheel:
15295       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15296       break;
15297
15298     case SOUND_boom:
15299       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15300       break;
15301
15302     case SOUND_die:
15303       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15304       break;
15305
15306     case SOUND_time:
15307       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15308       break;
15309
15310     default:
15311       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15312       break;
15313   }
15314 }
15315
15316 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15317 {
15318   int element = map_element_SP_to_RND(element_sp);
15319   int action = map_action_SP_to_RND(action_sp);
15320   int offset = (setup.sp_show_border_elements ? 0 : 1);
15321   int x = xx - offset;
15322   int y = yy - offset;
15323
15324   PlayLevelSoundElementAction(x, y, element, action);
15325 }
15326
15327 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15328 {
15329   int element = map_element_MM_to_RND(element_mm);
15330   int action = map_action_MM_to_RND(action_mm);
15331   int offset = 0;
15332   int x = xx - offset;
15333   int y = yy - offset;
15334
15335   if (!IS_MM_ELEMENT(element))
15336     element = EL_MM_DEFAULT;
15337
15338   PlayLevelSoundElementAction(x, y, element, action);
15339 }
15340
15341 void PlaySound_MM(int sound_mm)
15342 {
15343   int sound = map_sound_MM_to_RND(sound_mm);
15344
15345   if (sound == SND_UNDEFINED)
15346     return;
15347
15348   PlaySound(sound);
15349 }
15350
15351 void PlaySoundLoop_MM(int sound_mm)
15352 {
15353   int sound = map_sound_MM_to_RND(sound_mm);
15354
15355   if (sound == SND_UNDEFINED)
15356     return;
15357
15358   PlaySoundLoop(sound);
15359 }
15360
15361 void StopSound_MM(int sound_mm)
15362 {
15363   int sound = map_sound_MM_to_RND(sound_mm);
15364
15365   if (sound == SND_UNDEFINED)
15366     return;
15367
15368   StopSound(sound);
15369 }
15370
15371 void RaiseScore(int value)
15372 {
15373   game.score += value;
15374
15375   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15376
15377   DisplayGameControlValues();
15378 }
15379
15380 void RaiseScoreElement(int element)
15381 {
15382   switch (element)
15383   {
15384     case EL_EMERALD:
15385     case EL_BD_DIAMOND:
15386     case EL_EMERALD_YELLOW:
15387     case EL_EMERALD_RED:
15388     case EL_EMERALD_PURPLE:
15389     case EL_SP_INFOTRON:
15390       RaiseScore(level.score[SC_EMERALD]);
15391       break;
15392     case EL_DIAMOND:
15393       RaiseScore(level.score[SC_DIAMOND]);
15394       break;
15395     case EL_CRYSTAL:
15396       RaiseScore(level.score[SC_CRYSTAL]);
15397       break;
15398     case EL_PEARL:
15399       RaiseScore(level.score[SC_PEARL]);
15400       break;
15401     case EL_BUG:
15402     case EL_BD_BUTTERFLY:
15403     case EL_SP_ELECTRON:
15404       RaiseScore(level.score[SC_BUG]);
15405       break;
15406     case EL_SPACESHIP:
15407     case EL_BD_FIREFLY:
15408     case EL_SP_SNIKSNAK:
15409       RaiseScore(level.score[SC_SPACESHIP]);
15410       break;
15411     case EL_YAMYAM:
15412     case EL_DARK_YAMYAM:
15413       RaiseScore(level.score[SC_YAMYAM]);
15414       break;
15415     case EL_ROBOT:
15416       RaiseScore(level.score[SC_ROBOT]);
15417       break;
15418     case EL_PACMAN:
15419       RaiseScore(level.score[SC_PACMAN]);
15420       break;
15421     case EL_NUT:
15422       RaiseScore(level.score[SC_NUT]);
15423       break;
15424     case EL_DYNAMITE:
15425     case EL_EM_DYNAMITE:
15426     case EL_SP_DISK_RED:
15427     case EL_DYNABOMB_INCREASE_NUMBER:
15428     case EL_DYNABOMB_INCREASE_SIZE:
15429     case EL_DYNABOMB_INCREASE_POWER:
15430       RaiseScore(level.score[SC_DYNAMITE]);
15431       break;
15432     case EL_SHIELD_NORMAL:
15433     case EL_SHIELD_DEADLY:
15434       RaiseScore(level.score[SC_SHIELD]);
15435       break;
15436     case EL_EXTRA_TIME:
15437       RaiseScore(level.extra_time_score);
15438       break;
15439     case EL_KEY_1:
15440     case EL_KEY_2:
15441     case EL_KEY_3:
15442     case EL_KEY_4:
15443     case EL_EM_KEY_1:
15444     case EL_EM_KEY_2:
15445     case EL_EM_KEY_3:
15446     case EL_EM_KEY_4:
15447     case EL_EMC_KEY_5:
15448     case EL_EMC_KEY_6:
15449     case EL_EMC_KEY_7:
15450     case EL_EMC_KEY_8:
15451     case EL_DC_KEY_WHITE:
15452       RaiseScore(level.score[SC_KEY]);
15453       break;
15454     default:
15455       RaiseScore(element_info[element].collect_score);
15456       break;
15457   }
15458 }
15459
15460 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15461 {
15462   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15463   {
15464     if (!quick_quit)
15465     {
15466       // prevent short reactivation of overlay buttons while closing door
15467       SetOverlayActive(FALSE);
15468
15469       // door may still be open due to skipped or envelope style request
15470       CloseDoor(DOOR_CLOSE_1);
15471     }
15472
15473     if (network.enabled)
15474       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15475     else
15476     {
15477       if (quick_quit)
15478         FadeSkipNextFadeIn();
15479
15480       SetGameStatus(GAME_MODE_MAIN);
15481
15482       DrawMainMenu();
15483     }
15484   }
15485   else          // continue playing the game
15486   {
15487     if (tape.playing && tape.deactivate_display)
15488       TapeDeactivateDisplayOff(TRUE);
15489
15490     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15491
15492     if (tape.playing && tape.deactivate_display)
15493       TapeDeactivateDisplayOn();
15494   }
15495 }
15496
15497 void RequestQuitGame(boolean escape_key_pressed)
15498 {
15499   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15500   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15501                         level_editor_test_game);
15502   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15503                           quick_quit);
15504
15505   RequestQuitGameExt(skip_request, quick_quit,
15506                      "Do you really want to quit the game?");
15507 }
15508
15509 void RequestRestartGame(char *message)
15510 {
15511   game.restart_game_message = NULL;
15512
15513   boolean has_started_game = hasStartedNetworkGame();
15514   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15515
15516   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15517   {
15518     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15519   }
15520   else
15521   {
15522     // needed in case of envelope request to close game panel
15523     CloseDoor(DOOR_CLOSE_1);
15524
15525     SetGameStatus(GAME_MODE_MAIN);
15526
15527     DrawMainMenu();
15528   }
15529 }
15530
15531 void CheckGameOver(void)
15532 {
15533   static boolean last_game_over = FALSE;
15534   static int game_over_delay = 0;
15535   int game_over_delay_value = 50;
15536   boolean game_over = checkGameFailed();
15537
15538   // do not handle game over if request dialog is already active
15539   if (game.request_active)
15540     return;
15541
15542   // do not ask to play again if game was never actually played
15543   if (!game.GamePlayed)
15544     return;
15545
15546   if (!game_over)
15547   {
15548     last_game_over = FALSE;
15549     game_over_delay = game_over_delay_value;
15550
15551     return;
15552   }
15553
15554   if (game_over_delay > 0)
15555   {
15556     game_over_delay--;
15557
15558     return;
15559   }
15560
15561   if (last_game_over != game_over)
15562     game.restart_game_message = (hasStartedNetworkGame() ?
15563                                  "Game over! Play it again?" :
15564                                  "Game over!");
15565
15566   last_game_over = game_over;
15567 }
15568
15569 boolean checkGameSolved(void)
15570 {
15571   // set for all game engines if level was solved
15572   return game.LevelSolved_GameEnd;
15573 }
15574
15575 boolean checkGameFailed(void)
15576 {
15577   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15578     return (game_em.game_over && !game_em.level_solved);
15579   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15580     return (game_sp.game_over && !game_sp.level_solved);
15581   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15582     return (game_mm.game_over && !game_mm.level_solved);
15583   else                          // GAME_ENGINE_TYPE_RND
15584     return (game.GameOver && !game.LevelSolved);
15585 }
15586
15587 boolean checkGameEnded(void)
15588 {
15589   return (checkGameSolved() || checkGameFailed());
15590 }
15591
15592
15593 // ----------------------------------------------------------------------------
15594 // random generator functions
15595 // ----------------------------------------------------------------------------
15596
15597 unsigned int InitEngineRandom_RND(int seed)
15598 {
15599   game.num_random_calls = 0;
15600
15601   return InitEngineRandom(seed);
15602 }
15603
15604 unsigned int RND(int max)
15605 {
15606   if (max > 0)
15607   {
15608     game.num_random_calls++;
15609
15610     return GetEngineRandom(max);
15611   }
15612
15613   return 0;
15614 }
15615
15616
15617 // ----------------------------------------------------------------------------
15618 // game engine snapshot handling functions
15619 // ----------------------------------------------------------------------------
15620
15621 struct EngineSnapshotInfo
15622 {
15623   // runtime values for custom element collect score
15624   int collect_score[NUM_CUSTOM_ELEMENTS];
15625
15626   // runtime values for group element choice position
15627   int choice_pos[NUM_GROUP_ELEMENTS];
15628
15629   // runtime values for belt position animations
15630   int belt_graphic[4][NUM_BELT_PARTS];
15631   int belt_anim_mode[4][NUM_BELT_PARTS];
15632 };
15633
15634 static struct EngineSnapshotInfo engine_snapshot_rnd;
15635 static char *snapshot_level_identifier = NULL;
15636 static int snapshot_level_nr = -1;
15637
15638 static void SaveEngineSnapshotValues_RND(void)
15639 {
15640   static int belt_base_active_element[4] =
15641   {
15642     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15643     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15644     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15645     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15646   };
15647   int i, j;
15648
15649   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15650   {
15651     int element = EL_CUSTOM_START + i;
15652
15653     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15654   }
15655
15656   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15657   {
15658     int element = EL_GROUP_START + i;
15659
15660     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15661   }
15662
15663   for (i = 0; i < 4; i++)
15664   {
15665     for (j = 0; j < NUM_BELT_PARTS; j++)
15666     {
15667       int element = belt_base_active_element[i] + j;
15668       int graphic = el2img(element);
15669       int anim_mode = graphic_info[graphic].anim_mode;
15670
15671       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15672       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15673     }
15674   }
15675 }
15676
15677 static void LoadEngineSnapshotValues_RND(void)
15678 {
15679   unsigned int num_random_calls = game.num_random_calls;
15680   int i, j;
15681
15682   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15683   {
15684     int element = EL_CUSTOM_START + i;
15685
15686     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15687   }
15688
15689   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15690   {
15691     int element = EL_GROUP_START + i;
15692
15693     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15694   }
15695
15696   for (i = 0; i < 4; i++)
15697   {
15698     for (j = 0; j < NUM_BELT_PARTS; j++)
15699     {
15700       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15701       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15702
15703       graphic_info[graphic].anim_mode = anim_mode;
15704     }
15705   }
15706
15707   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15708   {
15709     InitRND(tape.random_seed);
15710     for (i = 0; i < num_random_calls; i++)
15711       RND(1);
15712   }
15713
15714   if (game.num_random_calls != num_random_calls)
15715   {
15716     Error("number of random calls out of sync");
15717     Error("number of random calls should be %d", num_random_calls);
15718     Error("number of random calls is %d", game.num_random_calls);
15719
15720     Fail("this should not happen -- please debug");
15721   }
15722 }
15723
15724 void FreeEngineSnapshotSingle(void)
15725 {
15726   FreeSnapshotSingle();
15727
15728   setString(&snapshot_level_identifier, NULL);
15729   snapshot_level_nr = -1;
15730 }
15731
15732 void FreeEngineSnapshotList(void)
15733 {
15734   FreeSnapshotList();
15735 }
15736
15737 static ListNode *SaveEngineSnapshotBuffers(void)
15738 {
15739   ListNode *buffers = NULL;
15740
15741   // copy some special values to a structure better suited for the snapshot
15742
15743   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15744     SaveEngineSnapshotValues_RND();
15745   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15746     SaveEngineSnapshotValues_EM();
15747   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15748     SaveEngineSnapshotValues_SP(&buffers);
15749   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15750     SaveEngineSnapshotValues_MM(&buffers);
15751
15752   // save values stored in special snapshot structure
15753
15754   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15755     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15756   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15757     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15758   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15759     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15760   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15761     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15762
15763   // save further RND engine values
15764
15765   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15766   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15767   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15768
15769   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15770   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15771   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15772   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15773   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15774
15775   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15776   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15777   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15778
15779   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15780
15781   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15782   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15783
15784   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15785   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15786   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15787   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15788   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15789   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15790   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15791   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15792   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15793   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15794   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15795   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15796   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15797   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15798   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15799   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15800   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15801   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15802
15803   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15804   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15805
15806   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15807   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15808   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15809
15810   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15811   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15812
15813   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15814   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15815   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15816   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15817   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15818
15819   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15820   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15821
15822 #if 0
15823   ListNode *node = engine_snapshot_list_rnd;
15824   int num_bytes = 0;
15825
15826   while (node != NULL)
15827   {
15828     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15829
15830     node = node->next;
15831   }
15832
15833   Debug("game:playing:SaveEngineSnapshotBuffers",
15834         "size of engine snapshot: %d bytes", num_bytes);
15835 #endif
15836
15837   return buffers;
15838 }
15839
15840 void SaveEngineSnapshotSingle(void)
15841 {
15842   ListNode *buffers = SaveEngineSnapshotBuffers();
15843
15844   // finally save all snapshot buffers to single snapshot
15845   SaveSnapshotSingle(buffers);
15846
15847   // save level identification information
15848   setString(&snapshot_level_identifier, leveldir_current->identifier);
15849   snapshot_level_nr = level_nr;
15850 }
15851
15852 boolean CheckSaveEngineSnapshotToList(void)
15853 {
15854   boolean save_snapshot =
15855     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15856      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15857       game.snapshot.changed_action) ||
15858      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15859       game.snapshot.collected_item));
15860
15861   game.snapshot.changed_action = FALSE;
15862   game.snapshot.collected_item = FALSE;
15863   game.snapshot.save_snapshot = save_snapshot;
15864
15865   return save_snapshot;
15866 }
15867
15868 void SaveEngineSnapshotToList(void)
15869 {
15870   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15871       tape.quick_resume)
15872     return;
15873
15874   ListNode *buffers = SaveEngineSnapshotBuffers();
15875
15876   // finally save all snapshot buffers to snapshot list
15877   SaveSnapshotToList(buffers);
15878 }
15879
15880 void SaveEngineSnapshotToListInitial(void)
15881 {
15882   FreeEngineSnapshotList();
15883
15884   SaveEngineSnapshotToList();
15885 }
15886
15887 static void LoadEngineSnapshotValues(void)
15888 {
15889   // restore special values from snapshot structure
15890
15891   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15892     LoadEngineSnapshotValues_RND();
15893   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15894     LoadEngineSnapshotValues_EM();
15895   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15896     LoadEngineSnapshotValues_SP();
15897   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15898     LoadEngineSnapshotValues_MM();
15899 }
15900
15901 void LoadEngineSnapshotSingle(void)
15902 {
15903   LoadSnapshotSingle();
15904
15905   LoadEngineSnapshotValues();
15906 }
15907
15908 static void LoadEngineSnapshot_Undo(int steps)
15909 {
15910   LoadSnapshotFromList_Older(steps);
15911
15912   LoadEngineSnapshotValues();
15913 }
15914
15915 static void LoadEngineSnapshot_Redo(int steps)
15916 {
15917   LoadSnapshotFromList_Newer(steps);
15918
15919   LoadEngineSnapshotValues();
15920 }
15921
15922 boolean CheckEngineSnapshotSingle(void)
15923 {
15924   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15925           snapshot_level_nr == level_nr);
15926 }
15927
15928 boolean CheckEngineSnapshotList(void)
15929 {
15930   return CheckSnapshotList();
15931 }
15932
15933
15934 // ---------- new game button stuff -------------------------------------------
15935
15936 static struct
15937 {
15938   int graphic;
15939   struct XY *pos;
15940   int gadget_id;
15941   boolean *setup_value;
15942   boolean allowed_on_tape;
15943   boolean is_touch_button;
15944   char *infotext;
15945 } gamebutton_info[NUM_GAME_BUTTONS] =
15946 {
15947   {
15948     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15949     GAME_CTRL_ID_STOP,                          NULL,
15950     TRUE, FALSE,                                "stop game"
15951   },
15952   {
15953     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15954     GAME_CTRL_ID_PAUSE,                         NULL,
15955     TRUE, FALSE,                                "pause game"
15956   },
15957   {
15958     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15959     GAME_CTRL_ID_PLAY,                          NULL,
15960     TRUE, FALSE,                                "play game"
15961   },
15962   {
15963     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15964     GAME_CTRL_ID_UNDO,                          NULL,
15965     TRUE, FALSE,                                "undo step"
15966   },
15967   {
15968     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15969     GAME_CTRL_ID_REDO,                          NULL,
15970     TRUE, FALSE,                                "redo step"
15971   },
15972   {
15973     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15974     GAME_CTRL_ID_SAVE,                          NULL,
15975     TRUE, FALSE,                                "save game"
15976   },
15977   {
15978     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15979     GAME_CTRL_ID_PAUSE2,                        NULL,
15980     TRUE, FALSE,                                "pause game"
15981   },
15982   {
15983     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15984     GAME_CTRL_ID_LOAD,                          NULL,
15985     TRUE, FALSE,                                "load game"
15986   },
15987   {
15988     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15989     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15990     FALSE, FALSE,                               "stop game"
15991   },
15992   {
15993     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15994     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15995     FALSE, FALSE,                               "pause game"
15996   },
15997   {
15998     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15999     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16000     FALSE, FALSE,                               "play game"
16001   },
16002   {
16003     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16004     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16005     FALSE, TRUE,                                "stop game"
16006   },
16007   {
16008     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16009     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16010     FALSE, TRUE,                                "pause game"
16011   },
16012   {
16013     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16014     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16015     TRUE, FALSE,                                "background music on/off"
16016   },
16017   {
16018     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16019     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16020     TRUE, FALSE,                                "sound loops on/off"
16021   },
16022   {
16023     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16024     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16025     TRUE, FALSE,                                "normal sounds on/off"
16026   },
16027   {
16028     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16029     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16030     FALSE, FALSE,                               "background music on/off"
16031   },
16032   {
16033     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16034     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16035     FALSE, FALSE,                               "sound loops on/off"
16036   },
16037   {
16038     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16039     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16040     FALSE, FALSE,                               "normal sounds on/off"
16041   }
16042 };
16043
16044 void CreateGameButtons(void)
16045 {
16046   int i;
16047
16048   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16049   {
16050     int graphic = gamebutton_info[i].graphic;
16051     struct GraphicInfo *gfx = &graphic_info[graphic];
16052     struct XY *pos = gamebutton_info[i].pos;
16053     struct GadgetInfo *gi;
16054     int button_type;
16055     boolean checked;
16056     unsigned int event_mask;
16057     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16058     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16059     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16060     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16061     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16062     int gd_x   = gfx->src_x;
16063     int gd_y   = gfx->src_y;
16064     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16065     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16066     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16067     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16068     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16069     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16070     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16071     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16072     int id = i;
16073
16074     if (gfx->bitmap == NULL)
16075     {
16076       game_gadget[id] = NULL;
16077
16078       continue;
16079     }
16080
16081     if (id == GAME_CTRL_ID_STOP ||
16082         id == GAME_CTRL_ID_PANEL_STOP ||
16083         id == GAME_CTRL_ID_TOUCH_STOP ||
16084         id == GAME_CTRL_ID_PLAY ||
16085         id == GAME_CTRL_ID_PANEL_PLAY ||
16086         id == GAME_CTRL_ID_SAVE ||
16087         id == GAME_CTRL_ID_LOAD)
16088     {
16089       button_type = GD_TYPE_NORMAL_BUTTON;
16090       checked = FALSE;
16091       event_mask = GD_EVENT_RELEASED;
16092     }
16093     else if (id == GAME_CTRL_ID_UNDO ||
16094              id == GAME_CTRL_ID_REDO)
16095     {
16096       button_type = GD_TYPE_NORMAL_BUTTON;
16097       checked = FALSE;
16098       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16099     }
16100     else
16101     {
16102       button_type = GD_TYPE_CHECK_BUTTON;
16103       checked = (gamebutton_info[i].setup_value != NULL ?
16104                  *gamebutton_info[i].setup_value : FALSE);
16105       event_mask = GD_EVENT_PRESSED;
16106     }
16107
16108     gi = CreateGadget(GDI_CUSTOM_ID, id,
16109                       GDI_IMAGE_ID, graphic,
16110                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16111                       GDI_X, base_x + x,
16112                       GDI_Y, base_y + y,
16113                       GDI_WIDTH, gfx->width,
16114                       GDI_HEIGHT, gfx->height,
16115                       GDI_TYPE, button_type,
16116                       GDI_STATE, GD_BUTTON_UNPRESSED,
16117                       GDI_CHECKED, checked,
16118                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16119                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16120                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16121                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16122                       GDI_DIRECT_DRAW, FALSE,
16123                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16124                       GDI_EVENT_MASK, event_mask,
16125                       GDI_CALLBACK_ACTION, HandleGameButtons,
16126                       GDI_END);
16127
16128     if (gi == NULL)
16129       Fail("cannot create gadget");
16130
16131     game_gadget[id] = gi;
16132   }
16133 }
16134
16135 void FreeGameButtons(void)
16136 {
16137   int i;
16138
16139   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16140     FreeGadget(game_gadget[i]);
16141 }
16142
16143 static void UnmapGameButtonsAtSamePosition(int id)
16144 {
16145   int i;
16146
16147   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16148     if (i != id &&
16149         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16150         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16151       UnmapGadget(game_gadget[i]);
16152 }
16153
16154 static void UnmapGameButtonsAtSamePosition_All(void)
16155 {
16156   if (setup.show_snapshot_buttons)
16157   {
16158     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16159     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16160     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16161   }
16162   else
16163   {
16164     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16165     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16166     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16167
16168     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16169     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16170     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16171   }
16172 }
16173
16174 static void MapGameButtonsAtSamePosition(int id)
16175 {
16176   int i;
16177
16178   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16179     if (i != id &&
16180         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16181         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16182       MapGadget(game_gadget[i]);
16183
16184   UnmapGameButtonsAtSamePosition_All();
16185 }
16186
16187 void MapUndoRedoButtons(void)
16188 {
16189   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16190   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16191
16192   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16193   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16194 }
16195
16196 void UnmapUndoRedoButtons(void)
16197 {
16198   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16199   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16200
16201   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16202   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16203 }
16204
16205 void ModifyPauseButtons(void)
16206 {
16207   static int ids[] =
16208   {
16209     GAME_CTRL_ID_PAUSE,
16210     GAME_CTRL_ID_PAUSE2,
16211     GAME_CTRL_ID_PANEL_PAUSE,
16212     GAME_CTRL_ID_TOUCH_PAUSE,
16213     -1
16214   };
16215   int i;
16216
16217   for (i = 0; ids[i] > -1; i++)
16218     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16219 }
16220
16221 static void MapGameButtonsExt(boolean on_tape)
16222 {
16223   int i;
16224
16225   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16226     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16227         i != GAME_CTRL_ID_UNDO &&
16228         i != GAME_CTRL_ID_REDO)
16229       MapGadget(game_gadget[i]);
16230
16231   UnmapGameButtonsAtSamePosition_All();
16232
16233   RedrawGameButtons();
16234 }
16235
16236 static void UnmapGameButtonsExt(boolean on_tape)
16237 {
16238   int i;
16239
16240   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16241     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16242       UnmapGadget(game_gadget[i]);
16243 }
16244
16245 static void RedrawGameButtonsExt(boolean on_tape)
16246 {
16247   int i;
16248
16249   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16250     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16251       RedrawGadget(game_gadget[i]);
16252 }
16253
16254 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16255 {
16256   if (gi == NULL)
16257     return;
16258
16259   gi->checked = state;
16260 }
16261
16262 static void RedrawSoundButtonGadget(int id)
16263 {
16264   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16265              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16266              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16267              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16268              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16269              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16270              id);
16271
16272   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16273   RedrawGadget(game_gadget[id2]);
16274 }
16275
16276 void MapGameButtons(void)
16277 {
16278   MapGameButtonsExt(FALSE);
16279 }
16280
16281 void UnmapGameButtons(void)
16282 {
16283   UnmapGameButtonsExt(FALSE);
16284 }
16285
16286 void RedrawGameButtons(void)
16287 {
16288   RedrawGameButtonsExt(FALSE);
16289 }
16290
16291 void MapGameButtonsOnTape(void)
16292 {
16293   MapGameButtonsExt(TRUE);
16294 }
16295
16296 void UnmapGameButtonsOnTape(void)
16297 {
16298   UnmapGameButtonsExt(TRUE);
16299 }
16300
16301 void RedrawGameButtonsOnTape(void)
16302 {
16303   RedrawGameButtonsExt(TRUE);
16304 }
16305
16306 static void GameUndoRedoExt(void)
16307 {
16308   ClearPlayerAction();
16309
16310   tape.pausing = TRUE;
16311
16312   RedrawPlayfield();
16313   UpdateAndDisplayGameControlValues();
16314
16315   DrawCompleteVideoDisplay();
16316   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16317   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16318   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16319
16320   BackToFront();
16321 }
16322
16323 static void GameUndo(int steps)
16324 {
16325   if (!CheckEngineSnapshotList())
16326     return;
16327
16328   LoadEngineSnapshot_Undo(steps);
16329
16330   GameUndoRedoExt();
16331 }
16332
16333 static void GameRedo(int steps)
16334 {
16335   if (!CheckEngineSnapshotList())
16336     return;
16337
16338   LoadEngineSnapshot_Redo(steps);
16339
16340   GameUndoRedoExt();
16341 }
16342
16343 static void HandleGameButtonsExt(int id, int button)
16344 {
16345   static boolean game_undo_executed = FALSE;
16346   int steps = BUTTON_STEPSIZE(button);
16347   boolean handle_game_buttons =
16348     (game_status == GAME_MODE_PLAYING ||
16349      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16350
16351   if (!handle_game_buttons)
16352     return;
16353
16354   switch (id)
16355   {
16356     case GAME_CTRL_ID_STOP:
16357     case GAME_CTRL_ID_PANEL_STOP:
16358     case GAME_CTRL_ID_TOUCH_STOP:
16359       if (game_status == GAME_MODE_MAIN)
16360         break;
16361
16362       if (tape.playing)
16363         TapeStop();
16364       else
16365         RequestQuitGame(FALSE);
16366
16367       break;
16368
16369     case GAME_CTRL_ID_PAUSE:
16370     case GAME_CTRL_ID_PAUSE2:
16371     case GAME_CTRL_ID_PANEL_PAUSE:
16372     case GAME_CTRL_ID_TOUCH_PAUSE:
16373       if (network.enabled && game_status == GAME_MODE_PLAYING)
16374       {
16375         if (tape.pausing)
16376           SendToServer_ContinuePlaying();
16377         else
16378           SendToServer_PausePlaying();
16379       }
16380       else
16381         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16382
16383       game_undo_executed = FALSE;
16384
16385       break;
16386
16387     case GAME_CTRL_ID_PLAY:
16388     case GAME_CTRL_ID_PANEL_PLAY:
16389       if (game_status == GAME_MODE_MAIN)
16390       {
16391         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16392       }
16393       else if (tape.pausing)
16394       {
16395         if (network.enabled)
16396           SendToServer_ContinuePlaying();
16397         else
16398           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16399       }
16400       break;
16401
16402     case GAME_CTRL_ID_UNDO:
16403       // Important: When using "save snapshot when collecting an item" mode,
16404       // load last (current) snapshot for first "undo" after pressing "pause"
16405       // (else the last-but-one snapshot would be loaded, because the snapshot
16406       // pointer already points to the last snapshot when pressing "pause",
16407       // which is fine for "every step/move" mode, but not for "every collect")
16408       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16409           !game_undo_executed)
16410         steps--;
16411
16412       game_undo_executed = TRUE;
16413
16414       GameUndo(steps);
16415       break;
16416
16417     case GAME_CTRL_ID_REDO:
16418       GameRedo(steps);
16419       break;
16420
16421     case GAME_CTRL_ID_SAVE:
16422       TapeQuickSave();
16423       break;
16424
16425     case GAME_CTRL_ID_LOAD:
16426       TapeQuickLoad();
16427       break;
16428
16429     case SOUND_CTRL_ID_MUSIC:
16430     case SOUND_CTRL_ID_PANEL_MUSIC:
16431       if (setup.sound_music)
16432       { 
16433         setup.sound_music = FALSE;
16434
16435         FadeMusic();
16436       }
16437       else if (audio.music_available)
16438       { 
16439         setup.sound = setup.sound_music = TRUE;
16440
16441         SetAudioMode(setup.sound);
16442
16443         if (game_status == GAME_MODE_PLAYING)
16444           PlayLevelMusic();
16445       }
16446
16447       RedrawSoundButtonGadget(id);
16448
16449       break;
16450
16451     case SOUND_CTRL_ID_LOOPS:
16452     case SOUND_CTRL_ID_PANEL_LOOPS:
16453       if (setup.sound_loops)
16454         setup.sound_loops = FALSE;
16455       else if (audio.loops_available)
16456       {
16457         setup.sound = setup.sound_loops = TRUE;
16458
16459         SetAudioMode(setup.sound);
16460       }
16461
16462       RedrawSoundButtonGadget(id);
16463
16464       break;
16465
16466     case SOUND_CTRL_ID_SIMPLE:
16467     case SOUND_CTRL_ID_PANEL_SIMPLE:
16468       if (setup.sound_simple)
16469         setup.sound_simple = FALSE;
16470       else if (audio.sound_available)
16471       {
16472         setup.sound = setup.sound_simple = TRUE;
16473
16474         SetAudioMode(setup.sound);
16475       }
16476
16477       RedrawSoundButtonGadget(id);
16478
16479       break;
16480
16481     default:
16482       break;
16483   }
16484 }
16485
16486 static void HandleGameButtons(struct GadgetInfo *gi)
16487 {
16488   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16489 }
16490
16491 void HandleSoundButtonKeys(Key key)
16492 {
16493   if (key == setup.shortcut.sound_simple)
16494     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16495   else if (key == setup.shortcut.sound_loops)
16496     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16497   else if (key == setup.shortcut.sound_music)
16498     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16499 }