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