d4c0402560342f08c47abe16dcef0883906d35f3
[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 CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1558
1559 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1560 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1561 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1562                                  IS_JUST_CHANGING(x, y))
1563
1564 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1565
1566 // static variables for playfield scan mode (scanning forward or backward)
1567 static int playfield_scan_start_x = 0;
1568 static int playfield_scan_start_y = 0;
1569 static int playfield_scan_delta_x = 1;
1570 static int playfield_scan_delta_y = 1;
1571
1572 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1573                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1574                                      (y) += playfield_scan_delta_y)     \
1575                                 for ((x) = playfield_scan_start_x;      \
1576                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1577                                      (x) += playfield_scan_delta_x)
1578
1579 #ifdef DEBUG
1580 void DEBUG_SetMaximumDynamite(void)
1581 {
1582   int i;
1583
1584   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1585     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1586       local_player->inventory_element[local_player->inventory_size++] =
1587         EL_DYNAMITE;
1588 }
1589 #endif
1590
1591 static void InitPlayfieldScanModeVars(void)
1592 {
1593   if (game.use_reverse_scan_direction)
1594   {
1595     playfield_scan_start_x = lev_fieldx - 1;
1596     playfield_scan_start_y = lev_fieldy - 1;
1597
1598     playfield_scan_delta_x = -1;
1599     playfield_scan_delta_y = -1;
1600   }
1601   else
1602   {
1603     playfield_scan_start_x = 0;
1604     playfield_scan_start_y = 0;
1605
1606     playfield_scan_delta_x = 1;
1607     playfield_scan_delta_y = 1;
1608   }
1609 }
1610
1611 static void InitPlayfieldScanMode(int mode)
1612 {
1613   game.use_reverse_scan_direction =
1614     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1615
1616   InitPlayfieldScanModeVars();
1617 }
1618
1619 static int get_move_delay_from_stepsize(int move_stepsize)
1620 {
1621   move_stepsize =
1622     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1623
1624   // make sure that stepsize value is always a power of 2
1625   move_stepsize = (1 << log_2(move_stepsize));
1626
1627   return TILEX / move_stepsize;
1628 }
1629
1630 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1631                                boolean init_game)
1632 {
1633   int player_nr = player->index_nr;
1634   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1635   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1636
1637   // do no immediately change move delay -- the player might just be moving
1638   player->move_delay_value_next = move_delay;
1639
1640   // information if player can move must be set separately
1641   player->cannot_move = cannot_move;
1642
1643   if (init_game)
1644   {
1645     player->move_delay       = game.initial_move_delay[player_nr];
1646     player->move_delay_value = game.initial_move_delay_value[player_nr];
1647
1648     player->move_delay_value_next = -1;
1649
1650     player->move_delay_reset_counter = 0;
1651   }
1652 }
1653
1654 void GetPlayerConfig(void)
1655 {
1656   GameFrameDelay = setup.game_frame_delay;
1657
1658   if (!audio.sound_available)
1659     setup.sound_simple = FALSE;
1660
1661   if (!audio.loops_available)
1662     setup.sound_loops = FALSE;
1663
1664   if (!audio.music_available)
1665     setup.sound_music = FALSE;
1666
1667   if (!video.fullscreen_available)
1668     setup.fullscreen = FALSE;
1669
1670   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1671
1672   SetAudioMode(setup.sound);
1673 }
1674
1675 int GetElementFromGroupElement(int element)
1676 {
1677   if (IS_GROUP_ELEMENT(element))
1678   {
1679     struct ElementGroupInfo *group = element_info[element].group;
1680     int last_anim_random_frame = gfx.anim_random_frame;
1681     int element_pos;
1682
1683     if (group->choice_mode == ANIM_RANDOM)
1684       gfx.anim_random_frame = RND(group->num_elements_resolved);
1685
1686     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1687                                     group->choice_mode, 0,
1688                                     group->choice_pos);
1689
1690     if (group->choice_mode == ANIM_RANDOM)
1691       gfx.anim_random_frame = last_anim_random_frame;
1692
1693     group->choice_pos++;
1694
1695     element = group->element_resolved[element_pos];
1696   }
1697
1698   return element;
1699 }
1700
1701 static void IncrementSokobanFieldsNeeded(void)
1702 {
1703   if (level.sb_fields_needed)
1704     game.sokoban_fields_still_needed++;
1705 }
1706
1707 static void IncrementSokobanObjectsNeeded(void)
1708 {
1709   if (level.sb_objects_needed)
1710     game.sokoban_objects_still_needed++;
1711 }
1712
1713 static void DecrementSokobanFieldsNeeded(void)
1714 {
1715   if (game.sokoban_fields_still_needed > 0)
1716     game.sokoban_fields_still_needed--;
1717 }
1718
1719 static void DecrementSokobanObjectsNeeded(void)
1720 {
1721   if (game.sokoban_objects_still_needed > 0)
1722     game.sokoban_objects_still_needed--;
1723 }
1724
1725 static void InitPlayerField(int x, int y, int element, boolean init_game)
1726 {
1727   if (element == EL_SP_MURPHY)
1728   {
1729     if (init_game)
1730     {
1731       if (stored_player[0].present)
1732       {
1733         Tile[x][y] = EL_SP_MURPHY_CLONE;
1734
1735         return;
1736       }
1737       else
1738       {
1739         stored_player[0].initial_element = element;
1740         stored_player[0].use_murphy = TRUE;
1741
1742         if (!level.use_artwork_element[0])
1743           stored_player[0].artwork_element = EL_SP_MURPHY;
1744       }
1745
1746       Tile[x][y] = EL_PLAYER_1;
1747     }
1748   }
1749
1750   if (init_game)
1751   {
1752     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1753     int jx = player->jx, jy = player->jy;
1754
1755     player->present = TRUE;
1756
1757     player->block_last_field = (element == EL_SP_MURPHY ?
1758                                 level.sp_block_last_field :
1759                                 level.block_last_field);
1760
1761     // ---------- initialize player's last field block delay ------------------
1762
1763     // always start with reliable default value (no adjustment needed)
1764     player->block_delay_adjustment = 0;
1765
1766     // special case 1: in Supaplex, Murphy blocks last field one more frame
1767     if (player->block_last_field && element == EL_SP_MURPHY)
1768       player->block_delay_adjustment = 1;
1769
1770     // special case 2: in game engines before 3.1.1, blocking was different
1771     if (game.use_block_last_field_bug)
1772       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1773
1774     if (!network.enabled || player->connected_network)
1775     {
1776       player->active = TRUE;
1777
1778       // remove potentially duplicate players
1779       if (StorePlayer[jx][jy] == Tile[x][y])
1780         StorePlayer[jx][jy] = 0;
1781
1782       StorePlayer[x][y] = Tile[x][y];
1783
1784 #if DEBUG_INIT_PLAYER
1785       Debug("game:init:player", "- player element %d activated",
1786             player->element_nr);
1787       Debug("game:init:player", "  (local player is %d and currently %s)",
1788             local_player->element_nr,
1789             local_player->active ? "active" : "not active");
1790     }
1791 #endif
1792
1793     Tile[x][y] = EL_EMPTY;
1794
1795     player->jx = player->last_jx = x;
1796     player->jy = player->last_jy = y;
1797   }
1798
1799   // always check if player was just killed and should be reanimated
1800   {
1801     int player_nr = GET_PLAYER_NR(element);
1802     struct PlayerInfo *player = &stored_player[player_nr];
1803
1804     if (player->active && player->killed)
1805       player->reanimated = TRUE; // if player was just killed, reanimate him
1806   }
1807 }
1808
1809 static void InitField(int x, int y, boolean init_game)
1810 {
1811   int element = Tile[x][y];
1812
1813   switch (element)
1814   {
1815     case EL_SP_MURPHY:
1816     case EL_PLAYER_1:
1817     case EL_PLAYER_2:
1818     case EL_PLAYER_3:
1819     case EL_PLAYER_4:
1820       InitPlayerField(x, y, element, init_game);
1821       break;
1822
1823     case EL_SOKOBAN_FIELD_PLAYER:
1824       element = Tile[x][y] = EL_PLAYER_1;
1825       InitField(x, y, init_game);
1826
1827       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1828       InitField(x, y, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_EMPTY:
1832       IncrementSokobanFieldsNeeded();
1833       break;
1834
1835     case EL_SOKOBAN_OBJECT:
1836       IncrementSokobanObjectsNeeded();
1837       break;
1838
1839     case EL_STONEBLOCK:
1840       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1841         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1842       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1843         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1844       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1845         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1846       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1847         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1848       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1849         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1850       break;
1851
1852     case EL_BUG:
1853     case EL_BUG_RIGHT:
1854     case EL_BUG_UP:
1855     case EL_BUG_LEFT:
1856     case EL_BUG_DOWN:
1857     case EL_SPACESHIP:
1858     case EL_SPACESHIP_RIGHT:
1859     case EL_SPACESHIP_UP:
1860     case EL_SPACESHIP_LEFT:
1861     case EL_SPACESHIP_DOWN:
1862     case EL_BD_BUTTERFLY:
1863     case EL_BD_BUTTERFLY_RIGHT:
1864     case EL_BD_BUTTERFLY_UP:
1865     case EL_BD_BUTTERFLY_LEFT:
1866     case EL_BD_BUTTERFLY_DOWN:
1867     case EL_BD_FIREFLY:
1868     case EL_BD_FIREFLY_RIGHT:
1869     case EL_BD_FIREFLY_UP:
1870     case EL_BD_FIREFLY_LEFT:
1871     case EL_BD_FIREFLY_DOWN:
1872     case EL_PACMAN_RIGHT:
1873     case EL_PACMAN_UP:
1874     case EL_PACMAN_LEFT:
1875     case EL_PACMAN_DOWN:
1876     case EL_YAMYAM:
1877     case EL_YAMYAM_LEFT:
1878     case EL_YAMYAM_RIGHT:
1879     case EL_YAMYAM_UP:
1880     case EL_YAMYAM_DOWN:
1881     case EL_DARK_YAMYAM:
1882     case EL_ROBOT:
1883     case EL_PACMAN:
1884     case EL_SP_SNIKSNAK:
1885     case EL_SP_ELECTRON:
1886     case EL_MOLE:
1887     case EL_MOLE_LEFT:
1888     case EL_MOLE_RIGHT:
1889     case EL_MOLE_UP:
1890     case EL_MOLE_DOWN:
1891     case EL_SPRING_LEFT:
1892     case EL_SPRING_RIGHT:
1893       InitMovDir(x, y);
1894       break;
1895
1896     case EL_AMOEBA_FULL:
1897     case EL_BD_AMOEBA:
1898       InitAmoebaNr(x, y);
1899       break;
1900
1901     case EL_AMOEBA_DROP:
1902       if (y == lev_fieldy - 1)
1903       {
1904         Tile[x][y] = EL_AMOEBA_GROWING;
1905         Store[x][y] = EL_AMOEBA_WET;
1906       }
1907       break;
1908
1909     case EL_DYNAMITE_ACTIVE:
1910     case EL_SP_DISK_RED_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1912     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1913     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1914     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1915       MovDelay[x][y] = 96;
1916       break;
1917
1918     case EL_EM_DYNAMITE_ACTIVE:
1919       MovDelay[x][y] = 32;
1920       break;
1921
1922     case EL_LAMP:
1923       game.lights_still_needed++;
1924       break;
1925
1926     case EL_PENGUIN:
1927       game.friends_still_needed++;
1928       break;
1929
1930     case EL_PIG:
1931     case EL_DRAGON:
1932       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1933       break;
1934
1935     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1944     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1945     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1946     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1947       if (init_game)
1948       {
1949         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1950         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1951         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1952
1953         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1954         {
1955           game.belt_dir[belt_nr] = belt_dir;
1956           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1957         }
1958         else    // more than one switch -- set it like the first switch
1959         {
1960           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1961         }
1962       }
1963       break;
1964
1965     case EL_LIGHT_SWITCH_ACTIVE:
1966       if (init_game)
1967         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1968       break;
1969
1970     case EL_INVISIBLE_STEELWALL:
1971     case EL_INVISIBLE_WALL:
1972     case EL_INVISIBLE_SAND:
1973       if (game.light_time_left > 0 ||
1974           game.lenses_time_left > 0)
1975         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1976       break;
1977
1978     case EL_EMC_MAGIC_BALL:
1979       if (game.ball_active)
1980         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1981       break;
1982
1983     case EL_EMC_MAGIC_BALL_SWITCH:
1984       if (game.ball_active)
1985         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1986       break;
1987
1988     case EL_TRIGGER_PLAYER:
1989     case EL_TRIGGER_ELEMENT:
1990     case EL_TRIGGER_CE_VALUE:
1991     case EL_TRIGGER_CE_SCORE:
1992     case EL_SELF:
1993     case EL_ANY_ELEMENT:
1994     case EL_CURRENT_CE_VALUE:
1995     case EL_CURRENT_CE_SCORE:
1996     case EL_PREV_CE_1:
1997     case EL_PREV_CE_2:
1998     case EL_PREV_CE_3:
1999     case EL_PREV_CE_4:
2000     case EL_PREV_CE_5:
2001     case EL_PREV_CE_6:
2002     case EL_PREV_CE_7:
2003     case EL_PREV_CE_8:
2004     case EL_NEXT_CE_1:
2005     case EL_NEXT_CE_2:
2006     case EL_NEXT_CE_3:
2007     case EL_NEXT_CE_4:
2008     case EL_NEXT_CE_5:
2009     case EL_NEXT_CE_6:
2010     case EL_NEXT_CE_7:
2011     case EL_NEXT_CE_8:
2012       // reference elements should not be used on the playfield
2013       Tile[x][y] = EL_EMPTY;
2014       break;
2015
2016     default:
2017       if (IS_CUSTOM_ELEMENT(element))
2018       {
2019         if (CAN_MOVE(element))
2020           InitMovDir(x, y);
2021
2022         if (!element_info[element].use_last_ce_value || init_game)
2023           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2024       }
2025       else if (IS_GROUP_ELEMENT(element))
2026       {
2027         Tile[x][y] = GetElementFromGroupElement(element);
2028
2029         InitField(x, y, init_game);
2030       }
2031
2032       break;
2033   }
2034
2035   if (!init_game)
2036     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2037 }
2038
2039 static void InitField_WithBug1(int x, int y, boolean init_game)
2040 {
2041   InitField(x, y, init_game);
2042
2043   // not needed to call InitMovDir() -- already done by InitField()!
2044   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2045       CAN_MOVE(Tile[x][y]))
2046     InitMovDir(x, y);
2047 }
2048
2049 static void InitField_WithBug2(int x, int y, boolean init_game)
2050 {
2051   int old_element = Tile[x][y];
2052
2053   InitField(x, y, init_game);
2054
2055   // not needed to call InitMovDir() -- already done by InitField()!
2056   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2057       CAN_MOVE(old_element) &&
2058       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2059     InitMovDir(x, y);
2060
2061   /* this case is in fact a combination of not less than three bugs:
2062      first, it calls InitMovDir() for elements that can move, although this is
2063      already done by InitField(); then, it checks the element that was at this
2064      field _before_ the call to InitField() (which can change it); lastly, it
2065      was not called for "mole with direction" elements, which were treated as
2066      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2067   */
2068 }
2069
2070 static int get_key_element_from_nr(int key_nr)
2071 {
2072   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2073                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2074                           EL_EM_KEY_1 : EL_KEY_1);
2075
2076   return key_base_element + key_nr;
2077 }
2078
2079 static int get_next_dropped_element(struct PlayerInfo *player)
2080 {
2081   return (player->inventory_size > 0 ?
2082           player->inventory_element[player->inventory_size - 1] :
2083           player->inventory_infinite_element != EL_UNDEFINED ?
2084           player->inventory_infinite_element :
2085           player->dynabombs_left > 0 ?
2086           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2087           EL_UNDEFINED);
2088 }
2089
2090 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2091 {
2092   // pos >= 0: get element from bottom of the stack;
2093   // pos <  0: get element from top of the stack
2094
2095   if (pos < 0)
2096   {
2097     int min_inventory_size = -pos;
2098     int inventory_pos = player->inventory_size - min_inventory_size;
2099     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2100
2101     return (player->inventory_size >= min_inventory_size ?
2102             player->inventory_element[inventory_pos] :
2103             player->inventory_infinite_element != EL_UNDEFINED ?
2104             player->inventory_infinite_element :
2105             player->dynabombs_left >= min_dynabombs_left ?
2106             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2107             EL_UNDEFINED);
2108   }
2109   else
2110   {
2111     int min_dynabombs_left = pos + 1;
2112     int min_inventory_size = pos + 1 - player->dynabombs_left;
2113     int inventory_pos = pos - player->dynabombs_left;
2114
2115     return (player->inventory_infinite_element != EL_UNDEFINED ?
2116             player->inventory_infinite_element :
2117             player->dynabombs_left >= min_dynabombs_left ?
2118             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2119             player->inventory_size >= min_inventory_size ?
2120             player->inventory_element[inventory_pos] :
2121             EL_UNDEFINED);
2122   }
2123 }
2124
2125 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2126 {
2127   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2128   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2129   int compare_result;
2130
2131   if (gpo1->sort_priority != gpo2->sort_priority)
2132     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2133   else
2134     compare_result = gpo1->nr - gpo2->nr;
2135
2136   return compare_result;
2137 }
2138
2139 int getPlayerInventorySize(int player_nr)
2140 {
2141   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2142     return game_em.ply[player_nr]->dynamite;
2143   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2144     return game_sp.red_disk_count;
2145   else
2146     return stored_player[player_nr].inventory_size;
2147 }
2148
2149 static void InitGameControlValues(void)
2150 {
2151   int i;
2152
2153   for (i = 0; game_panel_controls[i].nr != -1; i++)
2154   {
2155     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2156     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2157     struct TextPosInfo *pos = gpc->pos;
2158     int nr = gpc->nr;
2159     int type = gpc->type;
2160
2161     if (nr != i)
2162     {
2163       Error("'game_panel_controls' structure corrupted at %d", i);
2164
2165       Fail("this should not happen -- please debug");
2166     }
2167
2168     // force update of game controls after initialization
2169     gpc->value = gpc->last_value = -1;
2170     gpc->frame = gpc->last_frame = -1;
2171     gpc->gfx_frame = -1;
2172
2173     // determine panel value width for later calculation of alignment
2174     if (type == TYPE_INTEGER || type == TYPE_STRING)
2175     {
2176       pos->width = pos->size * getFontWidth(pos->font);
2177       pos->height = getFontHeight(pos->font);
2178     }
2179     else if (type == TYPE_ELEMENT)
2180     {
2181       pos->width = pos->size;
2182       pos->height = pos->size;
2183     }
2184
2185     // fill structure for game panel draw order
2186     gpo->nr = gpc->nr;
2187     gpo->sort_priority = pos->sort_priority;
2188   }
2189
2190   // sort game panel controls according to sort_priority and control number
2191   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2192         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2193 }
2194
2195 static void UpdatePlayfieldElementCount(void)
2196 {
2197   boolean use_element_count = FALSE;
2198   int i, j, x, y;
2199
2200   // first check if it is needed at all to calculate playfield element count
2201   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2202     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2203       use_element_count = TRUE;
2204
2205   if (!use_element_count)
2206     return;
2207
2208   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2209     element_info[i].element_count = 0;
2210
2211   SCAN_PLAYFIELD(x, y)
2212   {
2213     element_info[Tile[x][y]].element_count++;
2214   }
2215
2216   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2217     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2218       if (IS_IN_GROUP(j, i))
2219         element_info[EL_GROUP_START + i].element_count +=
2220           element_info[j].element_count;
2221 }
2222
2223 static void UpdateGameControlValues(void)
2224 {
2225   int i, k;
2226   int time = (game.LevelSolved ?
2227               game.LevelSolved_CountingTime :
2228               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229               game_em.lev->time :
2230               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2231               game_sp.time_played :
2232               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2233               game_mm.energy_left :
2234               game.no_time_limit ? TimePlayed : TimeLeft);
2235   int score = (game.LevelSolved ?
2236                game.LevelSolved_CountingScore :
2237                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2238                game_em.lev->score :
2239                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2240                game_sp.score :
2241                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2242                game_mm.score :
2243                game.score);
2244   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2245               game_em.lev->gems_needed :
2246               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2247               game_sp.infotrons_still_needed :
2248               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249               game_mm.kettles_still_needed :
2250               game.gems_still_needed);
2251   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2252                      game_em.lev->gems_needed > 0 :
2253                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2254                      game_sp.infotrons_still_needed > 0 :
2255                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2256                      game_mm.kettles_still_needed > 0 ||
2257                      game_mm.lights_still_needed > 0 :
2258                      game.gems_still_needed > 0 ||
2259                      game.sokoban_fields_still_needed > 0 ||
2260                      game.sokoban_objects_still_needed > 0 ||
2261                      game.lights_still_needed > 0);
2262   int health = (game.LevelSolved ?
2263                 game.LevelSolved_CountingHealth :
2264                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2265                 MM_HEALTH(game_mm.laser_overload_value) :
2266                 game.health);
2267   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2268
2269   UpdatePlayfieldElementCount();
2270
2271   // update game panel control values
2272
2273   // used instead of "level_nr" (for network games)
2274   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2275   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2276
2277   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2278   for (i = 0; i < MAX_NUM_KEYS; i++)
2279     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2280   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2281   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2282
2283   if (game.centered_player_nr == -1)
2284   {
2285     for (i = 0; i < MAX_PLAYERS; i++)
2286     {
2287       // only one player in Supaplex game engine
2288       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2289         break;
2290
2291       for (k = 0; k < MAX_NUM_KEYS; k++)
2292       {
2293         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2294         {
2295           if (game_em.ply[i]->keys & (1 << k))
2296             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2297               get_key_element_from_nr(k);
2298         }
2299         else if (stored_player[i].key[k])
2300           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2301             get_key_element_from_nr(k);
2302       }
2303
2304       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2305         getPlayerInventorySize(i);
2306
2307       if (stored_player[i].num_white_keys > 0)
2308         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2309           EL_DC_KEY_WHITE;
2310
2311       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2312         stored_player[i].num_white_keys;
2313     }
2314   }
2315   else
2316   {
2317     int player_nr = game.centered_player_nr;
2318
2319     for (k = 0; k < MAX_NUM_KEYS; k++)
2320     {
2321       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2322       {
2323         if (game_em.ply[player_nr]->keys & (1 << k))
2324           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2325             get_key_element_from_nr(k);
2326       }
2327       else if (stored_player[player_nr].key[k])
2328         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2329           get_key_element_from_nr(k);
2330     }
2331
2332     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2333       getPlayerInventorySize(player_nr);
2334
2335     if (stored_player[player_nr].num_white_keys > 0)
2336       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2337
2338     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2339       stored_player[player_nr].num_white_keys;
2340   }
2341
2342   // re-arrange keys on game panel, if needed or if defined by style settings
2343   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2344   {
2345     int nr = GAME_PANEL_KEY_1 + i;
2346     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2347     struct TextPosInfo *pos = gpc->pos;
2348
2349     // skip check if key is not in the player's inventory
2350     if (gpc->value == EL_EMPTY)
2351       continue;
2352
2353     // check if keys should be arranged on panel from left to right
2354     if (pos->style == STYLE_LEFTMOST_POSITION)
2355     {
2356       // check previous key positions (left from current key)
2357       for (k = 0; k < i; k++)
2358       {
2359         int nr_new = GAME_PANEL_KEY_1 + k;
2360
2361         if (game_panel_controls[nr_new].value == EL_EMPTY)
2362         {
2363           game_panel_controls[nr_new].value = gpc->value;
2364           gpc->value = EL_EMPTY;
2365
2366           break;
2367         }
2368       }
2369     }
2370
2371     // check if "undefined" keys can be placed at some other position
2372     if (pos->x == -1 && pos->y == -1)
2373     {
2374       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2375
2376       // 1st try: display key at the same position as normal or EM keys
2377       if (game_panel_controls[nr_new].value == EL_EMPTY)
2378       {
2379         game_panel_controls[nr_new].value = gpc->value;
2380       }
2381       else
2382       {
2383         // 2nd try: display key at the next free position in the key panel
2384         for (k = 0; k < STD_NUM_KEYS; k++)
2385         {
2386           nr_new = GAME_PANEL_KEY_1 + k;
2387
2388           if (game_panel_controls[nr_new].value == EL_EMPTY)
2389           {
2390             game_panel_controls[nr_new].value = gpc->value;
2391
2392             break;
2393           }
2394         }
2395       }
2396     }
2397   }
2398
2399   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2400   {
2401     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2402       get_inventory_element_from_pos(local_player, i);
2403     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2404       get_inventory_element_from_pos(local_player, -i - 1);
2405   }
2406
2407   game_panel_controls[GAME_PANEL_SCORE].value = score;
2408   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2409
2410   game_panel_controls[GAME_PANEL_TIME].value = time;
2411
2412   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2413   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2414   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2415
2416   if (level.time == 0)
2417     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2418   else
2419     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2420
2421   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2422   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2423
2424   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2425
2426   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2427     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2428      EL_EMPTY);
2429   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2430     local_player->shield_normal_time_left;
2431   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2432     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2433      EL_EMPTY);
2434   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2435     local_player->shield_deadly_time_left;
2436
2437   game_panel_controls[GAME_PANEL_EXIT].value =
2438     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2439
2440   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2441     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2442   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2443     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2444      EL_EMC_MAGIC_BALL_SWITCH);
2445
2446   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2447     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2448   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2449     game.light_time_left;
2450
2451   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2452     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2453   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2454     game.timegate_time_left;
2455
2456   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2457     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2458
2459   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2460     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2461   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2462     game.lenses_time_left;
2463
2464   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2465     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2466   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2467     game.magnify_time_left;
2468
2469   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2470     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2471      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2472      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2473      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2474      EL_BALLOON_SWITCH_NONE);
2475
2476   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2477     local_player->dynabomb_count;
2478   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2479     local_player->dynabomb_size;
2480   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2481     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2482
2483   game_panel_controls[GAME_PANEL_PENGUINS].value =
2484     game.friends_still_needed;
2485
2486   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2487     game.sokoban_objects_still_needed;
2488   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2489     game.sokoban_fields_still_needed;
2490
2491   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2492     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2493
2494   for (i = 0; i < NUM_BELTS; i++)
2495   {
2496     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2497       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2498        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2499     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2500       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2501   }
2502
2503   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2504     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2505   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2506     game.magic_wall_time_left;
2507
2508   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2509     local_player->gravity;
2510
2511   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2512     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2513
2514   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2515     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2516       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2517        game.panel.element[i].id : EL_UNDEFINED);
2518
2519   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2520     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2521       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2522        element_info[game.panel.element_count[i].id].element_count : 0);
2523
2524   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2525     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2526       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2527        element_info[game.panel.ce_score[i].id].collect_score : 0);
2528
2529   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2530     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2531       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2532        element_info[game.panel.ce_score_element[i].id].collect_score :
2533        EL_UNDEFINED);
2534
2535   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2536   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2537   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2538
2539   // update game panel control frames
2540
2541   for (i = 0; game_panel_controls[i].nr != -1; i++)
2542   {
2543     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2544
2545     if (gpc->type == TYPE_ELEMENT)
2546     {
2547       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2548       {
2549         int last_anim_random_frame = gfx.anim_random_frame;
2550         int element = gpc->value;
2551         int graphic = el2panelimg(element);
2552         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2553                                sync_random_frame : INIT_GFX_RANDOM());
2554
2555         if (gpc->value != gpc->last_value)
2556         {
2557           gpc->gfx_frame = 0;
2558           gpc->gfx_random = init_gfx_random;
2559         }
2560         else
2561         {
2562           gpc->gfx_frame++;
2563
2564           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2565               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2566             gpc->gfx_random = init_gfx_random;
2567         }
2568
2569         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2570           gfx.anim_random_frame = gpc->gfx_random;
2571
2572         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2573           gpc->gfx_frame = element_info[element].collect_score;
2574
2575         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2576
2577         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2578           gfx.anim_random_frame = last_anim_random_frame;
2579       }
2580     }
2581     else if (gpc->type == TYPE_GRAPHIC)
2582     {
2583       if (gpc->graphic != IMG_UNDEFINED)
2584       {
2585         int last_anim_random_frame = gfx.anim_random_frame;
2586         int graphic = gpc->graphic;
2587         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2588                                sync_random_frame : INIT_GFX_RANDOM());
2589
2590         if (gpc->value != gpc->last_value)
2591         {
2592           gpc->gfx_frame = 0;
2593           gpc->gfx_random = init_gfx_random;
2594         }
2595         else
2596         {
2597           gpc->gfx_frame++;
2598
2599           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2600               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2601             gpc->gfx_random = init_gfx_random;
2602         }
2603
2604         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2605           gfx.anim_random_frame = gpc->gfx_random;
2606
2607         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2608
2609         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2610           gfx.anim_random_frame = last_anim_random_frame;
2611       }
2612     }
2613   }
2614 }
2615
2616 static void DisplayGameControlValues(void)
2617 {
2618   boolean redraw_panel = FALSE;
2619   int i;
2620
2621   for (i = 0; game_panel_controls[i].nr != -1; i++)
2622   {
2623     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2624
2625     if (PANEL_DEACTIVATED(gpc->pos))
2626       continue;
2627
2628     if (gpc->value == gpc->last_value &&
2629         gpc->frame == gpc->last_frame)
2630       continue;
2631
2632     redraw_panel = TRUE;
2633   }
2634
2635   if (!redraw_panel)
2636     return;
2637
2638   // copy default game door content to main double buffer
2639
2640   // !!! CHECK AGAIN !!!
2641   SetPanelBackground();
2642   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2643   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2644
2645   // redraw game control buttons
2646   RedrawGameButtons();
2647
2648   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2649
2650   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2651   {
2652     int nr = game_panel_order[i].nr;
2653     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2654     struct TextPosInfo *pos = gpc->pos;
2655     int type = gpc->type;
2656     int value = gpc->value;
2657     int frame = gpc->frame;
2658     int size = pos->size;
2659     int font = pos->font;
2660     boolean draw_masked = pos->draw_masked;
2661     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2662
2663     if (PANEL_DEACTIVATED(pos))
2664       continue;
2665
2666     if (pos->class == get_hash_from_key("extra_panel_items") &&
2667         !setup.prefer_extra_panel_items)
2668       continue;
2669
2670     gpc->last_value = value;
2671     gpc->last_frame = frame;
2672
2673     if (type == TYPE_INTEGER)
2674     {
2675       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2676           nr == GAME_PANEL_TIME)
2677       {
2678         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2679
2680         if (use_dynamic_size)           // use dynamic number of digits
2681         {
2682           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2683           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2684           int size2 = size1 + 1;
2685           int font1 = pos->font;
2686           int font2 = pos->font_alt;
2687
2688           size = (value < value_change ? size1 : size2);
2689           font = (value < value_change ? font1 : font2);
2690         }
2691       }
2692
2693       // correct text size if "digits" is zero or less
2694       if (size <= 0)
2695         size = strlen(int2str(value, size));
2696
2697       // dynamically correct text alignment
2698       pos->width = size * getFontWidth(font);
2699
2700       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2701                   int2str(value, size), font, mask_mode);
2702     }
2703     else if (type == TYPE_ELEMENT)
2704     {
2705       int element, graphic;
2706       Bitmap *src_bitmap;
2707       int src_x, src_y;
2708       int width, height;
2709       int dst_x = PANEL_XPOS(pos);
2710       int dst_y = PANEL_YPOS(pos);
2711
2712       if (value != EL_UNDEFINED && value != EL_EMPTY)
2713       {
2714         element = value;
2715         graphic = el2panelimg(value);
2716
2717 #if 0
2718         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2719               element, EL_NAME(element), size);
2720 #endif
2721
2722         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2723           size = TILESIZE;
2724
2725         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2726                               &src_x, &src_y);
2727
2728         width  = graphic_info[graphic].width  * size / TILESIZE;
2729         height = graphic_info[graphic].height * size / TILESIZE;
2730
2731         if (draw_masked)
2732           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2733                            dst_x, dst_y);
2734         else
2735           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2736                      dst_x, dst_y);
2737       }
2738     }
2739     else if (type == TYPE_GRAPHIC)
2740     {
2741       int graphic        = gpc->graphic;
2742       int graphic_active = gpc->graphic_active;
2743       Bitmap *src_bitmap;
2744       int src_x, src_y;
2745       int width, height;
2746       int dst_x = PANEL_XPOS(pos);
2747       int dst_y = PANEL_YPOS(pos);
2748       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2749                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2750
2751       if (graphic != IMG_UNDEFINED && !skip)
2752       {
2753         if (pos->style == STYLE_REVERSE)
2754           value = 100 - value;
2755
2756         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2757
2758         if (pos->direction & MV_HORIZONTAL)
2759         {
2760           width  = graphic_info[graphic_active].width * value / 100;
2761           height = graphic_info[graphic_active].height;
2762
2763           if (pos->direction == MV_LEFT)
2764           {
2765             src_x += graphic_info[graphic_active].width - width;
2766             dst_x += graphic_info[graphic_active].width - width;
2767           }
2768         }
2769         else
2770         {
2771           width  = graphic_info[graphic_active].width;
2772           height = graphic_info[graphic_active].height * value / 100;
2773
2774           if (pos->direction == MV_UP)
2775           {
2776             src_y += graphic_info[graphic_active].height - height;
2777             dst_y += graphic_info[graphic_active].height - height;
2778           }
2779         }
2780
2781         if (draw_masked)
2782           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2783                            dst_x, dst_y);
2784         else
2785           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2786                      dst_x, dst_y);
2787
2788         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2789
2790         if (pos->direction & MV_HORIZONTAL)
2791         {
2792           if (pos->direction == MV_RIGHT)
2793           {
2794             src_x += width;
2795             dst_x += width;
2796           }
2797           else
2798           {
2799             dst_x = PANEL_XPOS(pos);
2800           }
2801
2802           width = graphic_info[graphic].width - width;
2803         }
2804         else
2805         {
2806           if (pos->direction == MV_DOWN)
2807           {
2808             src_y += height;
2809             dst_y += height;
2810           }
2811           else
2812           {
2813             dst_y = PANEL_YPOS(pos);
2814           }
2815
2816           height = graphic_info[graphic].height - height;
2817         }
2818
2819         if (draw_masked)
2820           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2821                            dst_x, dst_y);
2822         else
2823           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2824                      dst_x, dst_y);
2825       }
2826     }
2827     else if (type == TYPE_STRING)
2828     {
2829       boolean active = (value != 0);
2830       char *state_normal = "off";
2831       char *state_active = "on";
2832       char *state = (active ? state_active : state_normal);
2833       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2834                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2835                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2836                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2837
2838       if (nr == GAME_PANEL_GRAVITY_STATE)
2839       {
2840         int font1 = pos->font;          // (used for normal state)
2841         int font2 = pos->font_alt;      // (used for active state)
2842
2843         font = (active ? font2 : font1);
2844       }
2845
2846       if (s != NULL)
2847       {
2848         char *s_cut;
2849
2850         if (size <= 0)
2851         {
2852           // don't truncate output if "chars" is zero or less
2853           size = strlen(s);
2854
2855           // dynamically correct text alignment
2856           pos->width = size * getFontWidth(font);
2857         }
2858
2859         s_cut = getStringCopyN(s, size);
2860
2861         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2862                     s_cut, font, mask_mode);
2863
2864         free(s_cut);
2865       }
2866     }
2867
2868     redraw_mask |= REDRAW_DOOR_1;
2869   }
2870
2871   SetGameStatus(GAME_MODE_PLAYING);
2872 }
2873
2874 void UpdateAndDisplayGameControlValues(void)
2875 {
2876   if (tape.deactivate_display)
2877     return;
2878
2879   UpdateGameControlValues();
2880   DisplayGameControlValues();
2881 }
2882
2883 void UpdateGameDoorValues(void)
2884 {
2885   UpdateGameControlValues();
2886 }
2887
2888 void DrawGameDoorValues(void)
2889 {
2890   DisplayGameControlValues();
2891 }
2892
2893
2894 // ============================================================================
2895 // InitGameEngine()
2896 // ----------------------------------------------------------------------------
2897 // initialize game engine due to level / tape version number
2898 // ============================================================================
2899
2900 static void InitGameEngine(void)
2901 {
2902   int i, j, k, l, x, y;
2903
2904   // set game engine from tape file when re-playing, else from level file
2905   game.engine_version = (tape.playing ? tape.engine_version :
2906                          level.game_version);
2907
2908   // set single or multi-player game mode (needed for re-playing tapes)
2909   game.team_mode = setup.team_mode;
2910
2911   if (tape.playing)
2912   {
2913     int num_players = 0;
2914
2915     for (i = 0; i < MAX_PLAYERS; i++)
2916       if (tape.player_participates[i])
2917         num_players++;
2918
2919     // multi-player tapes contain input data for more than one player
2920     game.team_mode = (num_players > 1);
2921   }
2922
2923 #if 0
2924   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2925         level.game_version);
2926   Debug("game:init:level", "          tape.file_version   == %06d",
2927         tape.file_version);
2928   Debug("game:init:level", "          tape.game_version   == %06d",
2929         tape.game_version);
2930   Debug("game:init:level", "          tape.engine_version == %06d",
2931         tape.engine_version);
2932   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2933         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2934 #endif
2935
2936   // --------------------------------------------------------------------------
2937   // set flags for bugs and changes according to active game engine version
2938   // --------------------------------------------------------------------------
2939
2940   /*
2941     Summary of bugfix:
2942     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2943
2944     Bug was introduced in version:
2945     2.0.1
2946
2947     Bug was fixed in version:
2948     4.2.0.0
2949
2950     Description:
2951     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2952     but the property "can fall" was missing, which caused some levels to be
2953     unsolvable. This was fixed in version 4.2.0.0.
2954
2955     Affected levels/tapes:
2956     An example for a tape that was fixed by this bugfix is tape 029 from the
2957     level set "rnd_sam_bateman".
2958     The wrong behaviour will still be used for all levels or tapes that were
2959     created/recorded with it. An example for this is tape 023 from the level
2960     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2961   */
2962
2963   boolean use_amoeba_dropping_cannot_fall_bug =
2964     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2965       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2966      (tape.playing &&
2967       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2968       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2969
2970   /*
2971     Summary of bugfix/change:
2972     Fixed move speed of elements entering or leaving magic wall.
2973
2974     Fixed/changed in version:
2975     2.0.1
2976
2977     Description:
2978     Before 2.0.1, move speed of elements entering or leaving magic wall was
2979     twice as fast as it is now.
2980     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2981
2982     Affected levels/tapes:
2983     The first condition is generally needed for all levels/tapes before version
2984     2.0.1, which might use the old behaviour before it was changed; known tapes
2985     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2986     The second condition is an exception from the above case and is needed for
2987     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2988     above, but before it was known that this change would break tapes like the
2989     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2990     although the engine version while recording maybe was before 2.0.1. There
2991     are a lot of tapes that are affected by this exception, like tape 006 from
2992     the level set "rnd_conor_mancone".
2993   */
2994
2995   boolean use_old_move_stepsize_for_magic_wall =
2996     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2997      !(tape.playing &&
2998        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2999        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3000
3001   /*
3002     Summary of bugfix/change:
3003     Fixed handling for custom elements that change when pushed by the player.
3004
3005     Fixed/changed in version:
3006     3.1.0
3007
3008     Description:
3009     Before 3.1.0, custom elements that "change when pushing" changed directly
3010     after the player started pushing them (until then handled in "DigField()").
3011     Since 3.1.0, these custom elements are not changed until the "pushing"
3012     move of the element is finished (now handled in "ContinueMoving()").
3013
3014     Affected levels/tapes:
3015     The first condition is generally needed for all levels/tapes before version
3016     3.1.0, which might use the old behaviour before it was changed; known tapes
3017     that are affected are some tapes from the level set "Walpurgis Gardens" by
3018     Jamie Cullen.
3019     The second condition is an exception from the above case and is needed for
3020     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3021     above (including some development versions of 3.1.0), but before it was
3022     known that this change would break tapes like the above and was fixed in
3023     3.1.1, so that the changed behaviour was active although the engine version
3024     while recording maybe was before 3.1.0. There is at least one tape that is
3025     affected by this exception, which is the tape for the one-level set "Bug
3026     Machine" by Juergen Bonhagen.
3027   */
3028
3029   game.use_change_when_pushing_bug =
3030     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3031      !(tape.playing &&
3032        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3033        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3034
3035   /*
3036     Summary of bugfix/change:
3037     Fixed handling for blocking the field the player leaves when moving.
3038
3039     Fixed/changed in version:
3040     3.1.1
3041
3042     Description:
3043     Before 3.1.1, when "block last field when moving" was enabled, the field
3044     the player is leaving when moving was blocked for the time of the move,
3045     and was directly unblocked afterwards. This resulted in the last field
3046     being blocked for exactly one less than the number of frames of one player
3047     move. Additionally, even when blocking was disabled, the last field was
3048     blocked for exactly one frame.
3049     Since 3.1.1, due to changes in player movement handling, the last field
3050     is not blocked at all when blocking is disabled. When blocking is enabled,
3051     the last field is blocked for exactly the number of frames of one player
3052     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3053     last field is blocked for exactly one more than the number of frames of
3054     one player move.
3055
3056     Affected levels/tapes:
3057     (!!! yet to be determined -- probably many !!!)
3058   */
3059
3060   game.use_block_last_field_bug =
3061     (game.engine_version < VERSION_IDENT(3,1,1,0));
3062
3063   /* various special flags and settings for native Emerald Mine game engine */
3064
3065   game_em.use_single_button =
3066     (game.engine_version > VERSION_IDENT(4,0,0,2));
3067
3068   game_em.use_snap_key_bug =
3069     (game.engine_version < VERSION_IDENT(4,0,1,0));
3070
3071   game_em.use_random_bug =
3072     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3073
3074   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3075
3076   game_em.use_old_explosions            = use_old_em_engine;
3077   game_em.use_old_android               = use_old_em_engine;
3078   game_em.use_old_push_elements         = use_old_em_engine;
3079   game_em.use_old_push_into_acid        = use_old_em_engine;
3080
3081   game_em.use_wrap_around               = !use_old_em_engine;
3082
3083   // --------------------------------------------------------------------------
3084
3085   // set maximal allowed number of custom element changes per game frame
3086   game.max_num_changes_per_frame = 1;
3087
3088   // default scan direction: scan playfield from top/left to bottom/right
3089   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3090
3091   // dynamically adjust element properties according to game engine version
3092   InitElementPropertiesEngine(game.engine_version);
3093
3094   // ---------- initialize special element properties -------------------------
3095
3096   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3097   if (use_amoeba_dropping_cannot_fall_bug)
3098     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3099
3100   // ---------- initialize player's initial move delay ------------------------
3101
3102   // dynamically adjust player properties according to level information
3103   for (i = 0; i < MAX_PLAYERS; i++)
3104     game.initial_move_delay_value[i] =
3105       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3106
3107   // dynamically adjust player properties according to game engine version
3108   for (i = 0; i < MAX_PLAYERS; i++)
3109     game.initial_move_delay[i] =
3110       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3111        game.initial_move_delay_value[i] : 0);
3112
3113   // ---------- initialize player's initial push delay ------------------------
3114
3115   // dynamically adjust player properties according to game engine version
3116   game.initial_push_delay_value =
3117     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3118
3119   // ---------- initialize changing elements ----------------------------------
3120
3121   // initialize changing elements information
3122   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3123   {
3124     struct ElementInfo *ei = &element_info[i];
3125
3126     // this pointer might have been changed in the level editor
3127     ei->change = &ei->change_page[0];
3128
3129     if (!IS_CUSTOM_ELEMENT(i))
3130     {
3131       ei->change->target_element = EL_EMPTY_SPACE;
3132       ei->change->delay_fixed = 0;
3133       ei->change->delay_random = 0;
3134       ei->change->delay_frames = 1;
3135     }
3136
3137     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3138     {
3139       ei->has_change_event[j] = FALSE;
3140
3141       ei->event_page_nr[j] = 0;
3142       ei->event_page[j] = &ei->change_page[0];
3143     }
3144   }
3145
3146   // add changing elements from pre-defined list
3147   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3148   {
3149     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3150     struct ElementInfo *ei = &element_info[ch_delay->element];
3151
3152     ei->change->target_element       = ch_delay->target_element;
3153     ei->change->delay_fixed          = ch_delay->change_delay;
3154
3155     ei->change->pre_change_function  = ch_delay->pre_change_function;
3156     ei->change->change_function      = ch_delay->change_function;
3157     ei->change->post_change_function = ch_delay->post_change_function;
3158
3159     ei->change->can_change = TRUE;
3160     ei->change->can_change_or_has_action = TRUE;
3161
3162     ei->has_change_event[CE_DELAY] = TRUE;
3163
3164     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3165     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3166   }
3167
3168   // ---------- initialize internal run-time variables ------------------------
3169
3170   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3171   {
3172     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3173
3174     for (j = 0; j < ei->num_change_pages; j++)
3175     {
3176       ei->change_page[j].can_change_or_has_action =
3177         (ei->change_page[j].can_change |
3178          ei->change_page[j].has_action);
3179     }
3180   }
3181
3182   // add change events from custom element configuration
3183   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3184   {
3185     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3186
3187     for (j = 0; j < ei->num_change_pages; j++)
3188     {
3189       if (!ei->change_page[j].can_change_or_has_action)
3190         continue;
3191
3192       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3193       {
3194         // only add event page for the first page found with this event
3195         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3196         {
3197           ei->has_change_event[k] = TRUE;
3198
3199           ei->event_page_nr[k] = j;
3200           ei->event_page[k] = &ei->change_page[j];
3201         }
3202       }
3203     }
3204   }
3205
3206   // ---------- initialize reference elements in change conditions ------------
3207
3208   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3209   {
3210     int element = EL_CUSTOM_START + i;
3211     struct ElementInfo *ei = &element_info[element];
3212
3213     for (j = 0; j < ei->num_change_pages; j++)
3214     {
3215       int trigger_element = ei->change_page[j].initial_trigger_element;
3216
3217       if (trigger_element >= EL_PREV_CE_8 &&
3218           trigger_element <= EL_NEXT_CE_8)
3219         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3220
3221       ei->change_page[j].trigger_element = trigger_element;
3222     }
3223   }
3224
3225   // ---------- initialize run-time trigger player and element ----------------
3226
3227   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3228   {
3229     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3230
3231     for (j = 0; j < ei->num_change_pages; j++)
3232     {
3233       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3234       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3235       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3236       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3237       ei->change_page[j].actual_trigger_ce_value = 0;
3238       ei->change_page[j].actual_trigger_ce_score = 0;
3239     }
3240   }
3241
3242   // ---------- initialize trigger events -------------------------------------
3243
3244   // initialize trigger events information
3245   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3246     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3247       trigger_events[i][j] = FALSE;
3248
3249   // add trigger events from element change event properties
3250   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3251   {
3252     struct ElementInfo *ei = &element_info[i];
3253
3254     for (j = 0; j < ei->num_change_pages; j++)
3255     {
3256       if (!ei->change_page[j].can_change_or_has_action)
3257         continue;
3258
3259       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3260       {
3261         int trigger_element = ei->change_page[j].trigger_element;
3262
3263         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3264         {
3265           if (ei->change_page[j].has_event[k])
3266           {
3267             if (IS_GROUP_ELEMENT(trigger_element))
3268             {
3269               struct ElementGroupInfo *group =
3270                 element_info[trigger_element].group;
3271
3272               for (l = 0; l < group->num_elements_resolved; l++)
3273                 trigger_events[group->element_resolved[l]][k] = TRUE;
3274             }
3275             else if (trigger_element == EL_ANY_ELEMENT)
3276               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3277                 trigger_events[l][k] = TRUE;
3278             else
3279               trigger_events[trigger_element][k] = TRUE;
3280           }
3281         }
3282       }
3283     }
3284   }
3285
3286   // ---------- initialize push delay -----------------------------------------
3287
3288   // initialize push delay values to default
3289   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3290   {
3291     if (!IS_CUSTOM_ELEMENT(i))
3292     {
3293       // set default push delay values (corrected since version 3.0.7-1)
3294       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3295       {
3296         element_info[i].push_delay_fixed = 2;
3297         element_info[i].push_delay_random = 8;
3298       }
3299       else
3300       {
3301         element_info[i].push_delay_fixed = 8;
3302         element_info[i].push_delay_random = 8;
3303       }
3304     }
3305   }
3306
3307   // set push delay value for certain elements from pre-defined list
3308   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3309   {
3310     int e = push_delay_list[i].element;
3311
3312     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3313     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3314   }
3315
3316   // set push delay value for Supaplex elements for newer engine versions
3317   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3318   {
3319     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3320     {
3321       if (IS_SP_ELEMENT(i))
3322       {
3323         // set SP push delay to just enough to push under a falling zonk
3324         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3325
3326         element_info[i].push_delay_fixed  = delay;
3327         element_info[i].push_delay_random = 0;
3328       }
3329     }
3330   }
3331
3332   // ---------- initialize move stepsize --------------------------------------
3333
3334   // initialize move stepsize values to default
3335   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3336     if (!IS_CUSTOM_ELEMENT(i))
3337       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3338
3339   // set move stepsize value for certain elements from pre-defined list
3340   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3341   {
3342     int e = move_stepsize_list[i].element;
3343
3344     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3345
3346     // set move stepsize value for certain elements for older engine versions
3347     if (use_old_move_stepsize_for_magic_wall)
3348     {
3349       if (e == EL_MAGIC_WALL_FILLING ||
3350           e == EL_MAGIC_WALL_EMPTYING ||
3351           e == EL_BD_MAGIC_WALL_FILLING ||
3352           e == EL_BD_MAGIC_WALL_EMPTYING)
3353         element_info[e].move_stepsize *= 2;
3354     }
3355   }
3356
3357   // ---------- initialize collect score --------------------------------------
3358
3359   // initialize collect score values for custom elements from initial value
3360   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3361     if (IS_CUSTOM_ELEMENT(i))
3362       element_info[i].collect_score = element_info[i].collect_score_initial;
3363
3364   // ---------- initialize collect count --------------------------------------
3365
3366   // initialize collect count values for non-custom elements
3367   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3368     if (!IS_CUSTOM_ELEMENT(i))
3369       element_info[i].collect_count_initial = 0;
3370
3371   // add collect count values for all elements from pre-defined list
3372   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3373     element_info[collect_count_list[i].element].collect_count_initial =
3374       collect_count_list[i].count;
3375
3376   // ---------- initialize access direction -----------------------------------
3377
3378   // initialize access direction values to default (access from every side)
3379   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3380     if (!IS_CUSTOM_ELEMENT(i))
3381       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3382
3383   // set access direction value for certain elements from pre-defined list
3384   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3385     element_info[access_direction_list[i].element].access_direction =
3386       access_direction_list[i].direction;
3387
3388   // ---------- initialize explosion content ----------------------------------
3389   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3390   {
3391     if (IS_CUSTOM_ELEMENT(i))
3392       continue;
3393
3394     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3395     {
3396       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3397
3398       element_info[i].content.e[x][y] =
3399         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3400          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3401          i == EL_PLAYER_3 ? EL_EMERALD :
3402          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3403          i == EL_MOLE ? EL_EMERALD_RED :
3404          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3405          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3406          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3407          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3408          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3409          i == EL_WALL_EMERALD ? EL_EMERALD :
3410          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3411          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3412          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3413          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3414          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3415          i == EL_WALL_PEARL ? EL_PEARL :
3416          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3417          EL_EMPTY);
3418     }
3419   }
3420
3421   // ---------- initialize recursion detection --------------------------------
3422   recursion_loop_depth = 0;
3423   recursion_loop_detected = FALSE;
3424   recursion_loop_element = EL_UNDEFINED;
3425
3426   // ---------- initialize graphics engine ------------------------------------
3427   game.scroll_delay_value =
3428     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3429      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3430      !setup.forced_scroll_delay           ? 0 :
3431      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3432   game.scroll_delay_value =
3433     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3434
3435   // ---------- initialize game engine snapshots ------------------------------
3436   for (i = 0; i < MAX_PLAYERS; i++)
3437     game.snapshot.last_action[i] = 0;
3438   game.snapshot.changed_action = FALSE;
3439   game.snapshot.collected_item = FALSE;
3440   game.snapshot.mode =
3441     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3442      SNAPSHOT_MODE_EVERY_STEP :
3443      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3444      SNAPSHOT_MODE_EVERY_MOVE :
3445      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3446      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3447   game.snapshot.save_snapshot = FALSE;
3448
3449   // ---------- initialize level time for Supaplex engine ---------------------
3450   // Supaplex levels with time limit currently unsupported -- should be added
3451   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3452     level.time = 0;
3453
3454   // ---------- initialize flags for handling game actions --------------------
3455
3456   // set flags for game actions to default values
3457   game.use_key_actions = TRUE;
3458   game.use_mouse_actions = FALSE;
3459
3460   // when using Mirror Magic game engine, handle mouse events only
3461   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3462   {
3463     game.use_key_actions = FALSE;
3464     game.use_mouse_actions = TRUE;
3465   }
3466
3467   // check for custom elements with mouse click events
3468   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3469   {
3470     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3471     {
3472       int element = EL_CUSTOM_START + i;
3473
3474       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3475           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3476           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3477           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3478         game.use_mouse_actions = TRUE;
3479     }
3480   }
3481 }
3482
3483 static int get_num_special_action(int element, int action_first,
3484                                   int action_last)
3485 {
3486   int num_special_action = 0;
3487   int i, j;
3488
3489   for (i = action_first; i <= action_last; i++)
3490   {
3491     boolean found = FALSE;
3492
3493     for (j = 0; j < NUM_DIRECTIONS; j++)
3494       if (el_act_dir2img(element, i, j) !=
3495           el_act_dir2img(element, ACTION_DEFAULT, j))
3496         found = TRUE;
3497
3498     if (found)
3499       num_special_action++;
3500     else
3501       break;
3502   }
3503
3504   return num_special_action;
3505 }
3506
3507
3508 // ============================================================================
3509 // InitGame()
3510 // ----------------------------------------------------------------------------
3511 // initialize and start new game
3512 // ============================================================================
3513
3514 #if DEBUG_INIT_PLAYER
3515 static void DebugPrintPlayerStatus(char *message)
3516 {
3517   int i;
3518
3519   if (!options.debug)
3520     return;
3521
3522   Debug("game:init:player", "%s:", message);
3523
3524   for (i = 0; i < MAX_PLAYERS; i++)
3525   {
3526     struct PlayerInfo *player = &stored_player[i];
3527
3528     Debug("game:init:player",
3529           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3530           i + 1,
3531           player->present,
3532           player->connected,
3533           player->connected_locally,
3534           player->connected_network,
3535           player->active,
3536           (local_player == player ? " (local player)" : ""));
3537   }
3538 }
3539 #endif
3540
3541 void InitGame(void)
3542 {
3543   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3544   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3545   int fade_mask = REDRAW_FIELD;
3546
3547   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3548   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3549   int initial_move_dir = MV_DOWN;
3550   int i, j, x, y;
3551
3552   // required here to update video display before fading (FIX THIS)
3553   DrawMaskedBorder(REDRAW_DOOR_2);
3554
3555   if (!game.restart_level)
3556     CloseDoor(DOOR_CLOSE_1);
3557
3558   SetGameStatus(GAME_MODE_PLAYING);
3559
3560   if (level_editor_test_game)
3561     FadeSkipNextFadeOut();
3562   else
3563     FadeSetEnterScreen();
3564
3565   if (CheckFadeAll())
3566     fade_mask = REDRAW_ALL;
3567
3568   FadeLevelSoundsAndMusic();
3569
3570   ExpireSoundLoops(TRUE);
3571
3572   FadeOut(fade_mask);
3573
3574   if (level_editor_test_game)
3575     FadeSkipNextFadeIn();
3576
3577   // needed if different viewport properties defined for playing
3578   ChangeViewportPropertiesIfNeeded();
3579
3580   ClearField();
3581
3582   DrawCompleteVideoDisplay();
3583
3584   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3585
3586   InitGameEngine();
3587   InitGameControlValues();
3588
3589   if (tape.recording)
3590   {
3591     // initialize tape actions from game when recording tape
3592     tape.use_key_actions   = game.use_key_actions;
3593     tape.use_mouse_actions = game.use_mouse_actions;
3594
3595     // initialize visible playfield size when recording tape (for team mode)
3596     tape.scr_fieldx = SCR_FIELDX;
3597     tape.scr_fieldy = SCR_FIELDY;
3598   }
3599
3600   // don't play tapes over network
3601   network_playing = (network.enabled && !tape.playing);
3602
3603   for (i = 0; i < MAX_PLAYERS; i++)
3604   {
3605     struct PlayerInfo *player = &stored_player[i];
3606
3607     player->index_nr = i;
3608     player->index_bit = (1 << i);
3609     player->element_nr = EL_PLAYER_1 + i;
3610
3611     player->present = FALSE;
3612     player->active = FALSE;
3613     player->mapped = FALSE;
3614
3615     player->killed = FALSE;
3616     player->reanimated = FALSE;
3617     player->buried = FALSE;
3618
3619     player->action = 0;
3620     player->effective_action = 0;
3621     player->programmed_action = 0;
3622     player->snap_action = 0;
3623
3624     player->mouse_action.lx = 0;
3625     player->mouse_action.ly = 0;
3626     player->mouse_action.button = 0;
3627     player->mouse_action.button_hint = 0;
3628
3629     player->effective_mouse_action.lx = 0;
3630     player->effective_mouse_action.ly = 0;
3631     player->effective_mouse_action.button = 0;
3632     player->effective_mouse_action.button_hint = 0;
3633
3634     for (j = 0; j < MAX_NUM_KEYS; j++)
3635       player->key[j] = FALSE;
3636
3637     player->num_white_keys = 0;
3638
3639     player->dynabomb_count = 0;
3640     player->dynabomb_size = 1;
3641     player->dynabombs_left = 0;
3642     player->dynabomb_xl = FALSE;
3643
3644     player->MovDir = initial_move_dir;
3645     player->MovPos = 0;
3646     player->GfxPos = 0;
3647     player->GfxDir = initial_move_dir;
3648     player->GfxAction = ACTION_DEFAULT;
3649     player->Frame = 0;
3650     player->StepFrame = 0;
3651
3652     player->initial_element = player->element_nr;
3653     player->artwork_element =
3654       (level.use_artwork_element[i] ? level.artwork_element[i] :
3655        player->element_nr);
3656     player->use_murphy = FALSE;
3657
3658     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3659     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3660
3661     player->gravity = level.initial_player_gravity[i];
3662
3663     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3664
3665     player->actual_frame_counter = 0;
3666
3667     player->step_counter = 0;
3668
3669     player->last_move_dir = initial_move_dir;
3670
3671     player->is_active = FALSE;
3672
3673     player->is_waiting = FALSE;
3674     player->is_moving = FALSE;
3675     player->is_auto_moving = FALSE;
3676     player->is_digging = FALSE;
3677     player->is_snapping = FALSE;
3678     player->is_collecting = FALSE;
3679     player->is_pushing = FALSE;
3680     player->is_switching = FALSE;
3681     player->is_dropping = FALSE;
3682     player->is_dropping_pressed = FALSE;
3683
3684     player->is_bored = FALSE;
3685     player->is_sleeping = FALSE;
3686
3687     player->was_waiting = TRUE;
3688     player->was_moving = FALSE;
3689     player->was_snapping = FALSE;
3690     player->was_dropping = FALSE;
3691
3692     player->force_dropping = FALSE;
3693
3694     player->frame_counter_bored = -1;
3695     player->frame_counter_sleeping = -1;
3696
3697     player->anim_delay_counter = 0;
3698     player->post_delay_counter = 0;
3699
3700     player->dir_waiting = initial_move_dir;
3701     player->action_waiting = ACTION_DEFAULT;
3702     player->last_action_waiting = ACTION_DEFAULT;
3703     player->special_action_bored = ACTION_DEFAULT;
3704     player->special_action_sleeping = ACTION_DEFAULT;
3705
3706     player->switch_x = -1;
3707     player->switch_y = -1;
3708
3709     player->drop_x = -1;
3710     player->drop_y = -1;
3711
3712     player->show_envelope = 0;
3713
3714     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3715
3716     player->push_delay       = -1;      // initialized when pushing starts
3717     player->push_delay_value = game.initial_push_delay_value;
3718
3719     player->drop_delay = 0;
3720     player->drop_pressed_delay = 0;
3721
3722     player->last_jx = -1;
3723     player->last_jy = -1;
3724     player->jx = -1;
3725     player->jy = -1;
3726
3727     player->shield_normal_time_left = 0;
3728     player->shield_deadly_time_left = 0;
3729
3730     player->last_removed_element = EL_UNDEFINED;
3731
3732     player->inventory_infinite_element = EL_UNDEFINED;
3733     player->inventory_size = 0;
3734
3735     if (level.use_initial_inventory[i])
3736     {
3737       for (j = 0; j < level.initial_inventory_size[i]; j++)
3738       {
3739         int element = level.initial_inventory_content[i][j];
3740         int collect_count = element_info[element].collect_count_initial;
3741         int k;
3742
3743         if (!IS_CUSTOM_ELEMENT(element))
3744           collect_count = 1;
3745
3746         if (collect_count == 0)
3747           player->inventory_infinite_element = element;
3748         else
3749           for (k = 0; k < collect_count; k++)
3750             if (player->inventory_size < MAX_INVENTORY_SIZE)
3751               player->inventory_element[player->inventory_size++] = element;
3752       }
3753     }
3754
3755     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3756     SnapField(player, 0, 0);
3757
3758     map_player_action[i] = i;
3759   }
3760
3761   network_player_action_received = FALSE;
3762
3763   // initial null action
3764   if (network_playing)
3765     SendToServer_MovePlayer(MV_NONE);
3766
3767   FrameCounter = 0;
3768   TimeFrames = 0;
3769   TimePlayed = 0;
3770   TimeLeft = level.time;
3771   TapeTime = 0;
3772
3773   ScreenMovDir = MV_NONE;
3774   ScreenMovPos = 0;
3775   ScreenGfxPos = 0;
3776
3777   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3778
3779   game.robot_wheel_x = -1;
3780   game.robot_wheel_y = -1;
3781
3782   game.exit_x = -1;
3783   game.exit_y = -1;
3784
3785   game.all_players_gone = FALSE;
3786
3787   game.LevelSolved = FALSE;
3788   game.GameOver = FALSE;
3789
3790   game.GamePlayed = !tape.playing;
3791
3792   game.LevelSolved_GameWon = FALSE;
3793   game.LevelSolved_GameEnd = FALSE;
3794   game.LevelSolved_SaveTape = FALSE;
3795   game.LevelSolved_SaveScore = FALSE;
3796
3797   game.LevelSolved_CountingTime = 0;
3798   game.LevelSolved_CountingScore = 0;
3799   game.LevelSolved_CountingHealth = 0;
3800
3801   game.panel.active = TRUE;
3802
3803   game.no_time_limit = (level.time == 0);
3804
3805   game.yamyam_content_nr = 0;
3806   game.robot_wheel_active = FALSE;
3807   game.magic_wall_active = FALSE;
3808   game.magic_wall_time_left = 0;
3809   game.light_time_left = 0;
3810   game.timegate_time_left = 0;
3811   game.switchgate_pos = 0;
3812   game.wind_direction = level.wind_direction_initial;
3813
3814   game.time_final = 0;
3815   game.score_time_final = 0;
3816
3817   game.score = 0;
3818   game.score_final = 0;
3819
3820   game.health = MAX_HEALTH;
3821   game.health_final = MAX_HEALTH;
3822
3823   game.gems_still_needed = level.gems_needed;
3824   game.sokoban_fields_still_needed = 0;
3825   game.sokoban_objects_still_needed = 0;
3826   game.lights_still_needed = 0;
3827   game.players_still_needed = 0;
3828   game.friends_still_needed = 0;
3829
3830   game.lenses_time_left = 0;
3831   game.magnify_time_left = 0;
3832
3833   game.ball_active = level.ball_active_initial;
3834   game.ball_content_nr = 0;
3835
3836   game.explosions_delayed = TRUE;
3837
3838   game.envelope_active = FALSE;
3839
3840   for (i = 0; i < NUM_BELTS; i++)
3841   {
3842     game.belt_dir[i] = MV_NONE;
3843     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3844   }
3845
3846   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3847     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3848
3849 #if DEBUG_INIT_PLAYER
3850   DebugPrintPlayerStatus("Player status at level initialization");
3851 #endif
3852
3853   SCAN_PLAYFIELD(x, y)
3854   {
3855     Tile[x][y] = Last[x][y] = level.field[x][y];
3856     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3857     ChangeDelay[x][y] = 0;
3858     ChangePage[x][y] = -1;
3859     CustomValue[x][y] = 0;              // initialized in InitField()
3860     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3861     AmoebaNr[x][y] = 0;
3862     WasJustMoving[x][y] = 0;
3863     WasJustFalling[x][y] = 0;
3864     CheckCollision[x][y] = 0;
3865     CheckImpact[x][y] = 0;
3866     Stop[x][y] = FALSE;
3867     Pushed[x][y] = FALSE;
3868
3869     ChangeCount[x][y] = 0;
3870     ChangeEvent[x][y] = -1;
3871
3872     ExplodePhase[x][y] = 0;
3873     ExplodeDelay[x][y] = 0;
3874     ExplodeField[x][y] = EX_TYPE_NONE;
3875
3876     RunnerVisit[x][y] = 0;
3877     PlayerVisit[x][y] = 0;
3878
3879     GfxFrame[x][y] = 0;
3880     GfxRandom[x][y] = INIT_GFX_RANDOM();
3881     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3882     GfxElement[x][y] = EL_UNDEFINED;
3883     GfxAction[x][y] = ACTION_DEFAULT;
3884     GfxDir[x][y] = MV_NONE;
3885     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3886   }
3887
3888   SCAN_PLAYFIELD(x, y)
3889   {
3890     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3891       emulate_bd = FALSE;
3892     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3893       emulate_sp = FALSE;
3894
3895     InitField(x, y, TRUE);
3896
3897     ResetGfxAnimation(x, y);
3898   }
3899
3900   InitBeltMovement();
3901
3902   for (i = 0; i < MAX_PLAYERS; i++)
3903   {
3904     struct PlayerInfo *player = &stored_player[i];
3905
3906     // set number of special actions for bored and sleeping animation
3907     player->num_special_action_bored =
3908       get_num_special_action(player->artwork_element,
3909                              ACTION_BORING_1, ACTION_BORING_LAST);
3910     player->num_special_action_sleeping =
3911       get_num_special_action(player->artwork_element,
3912                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3913   }
3914
3915   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3916                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3917
3918   // initialize type of slippery elements
3919   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3920   {
3921     if (!IS_CUSTOM_ELEMENT(i))
3922     {
3923       // default: elements slip down either to the left or right randomly
3924       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3925
3926       // SP style elements prefer to slip down on the left side
3927       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3928         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3929
3930       // BD style elements prefer to slip down on the left side
3931       if (game.emulation == EMU_BOULDERDASH)
3932         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3933     }
3934   }
3935
3936   // initialize explosion and ignition delay
3937   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3938   {
3939     if (!IS_CUSTOM_ELEMENT(i))
3940     {
3941       int num_phase = 8;
3942       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3943                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3944                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3945       int last_phase = (num_phase + 1) * delay;
3946       int half_phase = (num_phase / 2) * delay;
3947
3948       element_info[i].explosion_delay = last_phase - 1;
3949       element_info[i].ignition_delay = half_phase;
3950
3951       if (i == EL_BLACK_ORB)
3952         element_info[i].ignition_delay = 1;
3953     }
3954   }
3955
3956   // correct non-moving belts to start moving left
3957   for (i = 0; i < NUM_BELTS; i++)
3958     if (game.belt_dir[i] == MV_NONE)
3959       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3960
3961 #if USE_NEW_PLAYER_ASSIGNMENTS
3962   // use preferred player also in local single-player mode
3963   if (!network.enabled && !game.team_mode)
3964   {
3965     int new_index_nr = setup.network_player_nr;
3966
3967     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3968     {
3969       for (i = 0; i < MAX_PLAYERS; i++)
3970         stored_player[i].connected_locally = FALSE;
3971
3972       stored_player[new_index_nr].connected_locally = TRUE;
3973     }
3974   }
3975
3976   for (i = 0; i < MAX_PLAYERS; i++)
3977   {
3978     stored_player[i].connected = FALSE;
3979
3980     // in network game mode, the local player might not be the first player
3981     if (stored_player[i].connected_locally)
3982       local_player = &stored_player[i];
3983   }
3984
3985   if (!network.enabled)
3986     local_player->connected = TRUE;
3987
3988   if (tape.playing)
3989   {
3990     for (i = 0; i < MAX_PLAYERS; i++)
3991       stored_player[i].connected = tape.player_participates[i];
3992   }
3993   else if (network.enabled)
3994   {
3995     // add team mode players connected over the network (needed for correct
3996     // assignment of player figures from level to locally playing players)
3997
3998     for (i = 0; i < MAX_PLAYERS; i++)
3999       if (stored_player[i].connected_network)
4000         stored_player[i].connected = TRUE;
4001   }
4002   else if (game.team_mode)
4003   {
4004     // try to guess locally connected team mode players (needed for correct
4005     // assignment of player figures from level to locally playing players)
4006
4007     for (i = 0; i < MAX_PLAYERS; i++)
4008       if (setup.input[i].use_joystick ||
4009           setup.input[i].key.left != KSYM_UNDEFINED)
4010         stored_player[i].connected = TRUE;
4011   }
4012
4013 #if DEBUG_INIT_PLAYER
4014   DebugPrintPlayerStatus("Player status after level initialization");
4015 #endif
4016
4017 #if DEBUG_INIT_PLAYER
4018   Debug("game:init:player", "Reassigning players ...");
4019 #endif
4020
4021   // check if any connected player was not found in playfield
4022   for (i = 0; i < MAX_PLAYERS; i++)
4023   {
4024     struct PlayerInfo *player = &stored_player[i];
4025
4026     if (player->connected && !player->present)
4027     {
4028       struct PlayerInfo *field_player = NULL;
4029
4030 #if DEBUG_INIT_PLAYER
4031       Debug("game:init:player",
4032             "- looking for field player for player %d ...", i + 1);
4033 #endif
4034
4035       // assign first free player found that is present in the playfield
4036
4037       // first try: look for unmapped playfield player that is not connected
4038       for (j = 0; j < MAX_PLAYERS; j++)
4039         if (field_player == NULL &&
4040             stored_player[j].present &&
4041             !stored_player[j].mapped &&
4042             !stored_player[j].connected)
4043           field_player = &stored_player[j];
4044
4045       // second try: look for *any* unmapped playfield player
4046       for (j = 0; j < MAX_PLAYERS; j++)
4047         if (field_player == NULL &&
4048             stored_player[j].present &&
4049             !stored_player[j].mapped)
4050           field_player = &stored_player[j];
4051
4052       if (field_player != NULL)
4053       {
4054         int jx = field_player->jx, jy = field_player->jy;
4055
4056 #if DEBUG_INIT_PLAYER
4057         Debug("game:init:player", "- found player %d",
4058               field_player->index_nr + 1);
4059 #endif
4060
4061         player->present = FALSE;
4062         player->active = FALSE;
4063
4064         field_player->present = TRUE;
4065         field_player->active = TRUE;
4066
4067         /*
4068         player->initial_element = field_player->initial_element;
4069         player->artwork_element = field_player->artwork_element;
4070
4071         player->block_last_field       = field_player->block_last_field;
4072         player->block_delay_adjustment = field_player->block_delay_adjustment;
4073         */
4074
4075         StorePlayer[jx][jy] = field_player->element_nr;
4076
4077         field_player->jx = field_player->last_jx = jx;
4078         field_player->jy = field_player->last_jy = jy;
4079
4080         if (local_player == player)
4081           local_player = field_player;
4082
4083         map_player_action[field_player->index_nr] = i;
4084
4085         field_player->mapped = TRUE;
4086
4087 #if DEBUG_INIT_PLAYER
4088         Debug("game:init:player", "- map_player_action[%d] == %d",
4089               field_player->index_nr + 1, i + 1);
4090 #endif
4091       }
4092     }
4093
4094     if (player->connected && player->present)
4095       player->mapped = TRUE;
4096   }
4097
4098 #if DEBUG_INIT_PLAYER
4099   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4100 #endif
4101
4102 #else
4103
4104   // check if any connected player was not found in playfield
4105   for (i = 0; i < MAX_PLAYERS; i++)
4106   {
4107     struct PlayerInfo *player = &stored_player[i];
4108
4109     if (player->connected && !player->present)
4110     {
4111       for (j = 0; j < MAX_PLAYERS; j++)
4112       {
4113         struct PlayerInfo *field_player = &stored_player[j];
4114         int jx = field_player->jx, jy = field_player->jy;
4115
4116         // assign first free player found that is present in the playfield
4117         if (field_player->present && !field_player->connected)
4118         {
4119           player->present = TRUE;
4120           player->active = TRUE;
4121
4122           field_player->present = FALSE;
4123           field_player->active = FALSE;
4124
4125           player->initial_element = field_player->initial_element;
4126           player->artwork_element = field_player->artwork_element;
4127
4128           player->block_last_field       = field_player->block_last_field;
4129           player->block_delay_adjustment = field_player->block_delay_adjustment;
4130
4131           StorePlayer[jx][jy] = player->element_nr;
4132
4133           player->jx = player->last_jx = jx;
4134           player->jy = player->last_jy = jy;
4135
4136           break;
4137         }
4138       }
4139     }
4140   }
4141 #endif
4142
4143 #if 0
4144   Debug("game:init:player", "local_player->present == %d",
4145         local_player->present);
4146 #endif
4147
4148   // set focus to local player for network games, else to all players
4149   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4150   game.centered_player_nr_next = game.centered_player_nr;
4151   game.set_centered_player = FALSE;
4152   game.set_centered_player_wrap = FALSE;
4153
4154   if (network_playing && tape.recording)
4155   {
4156     // store client dependent player focus when recording network games
4157     tape.centered_player_nr_next = game.centered_player_nr_next;
4158     tape.set_centered_player = TRUE;
4159   }
4160
4161   if (tape.playing)
4162   {
4163     // when playing a tape, eliminate all players who do not participate
4164
4165 #if USE_NEW_PLAYER_ASSIGNMENTS
4166
4167     if (!game.team_mode)
4168     {
4169       for (i = 0; i < MAX_PLAYERS; i++)
4170       {
4171         if (stored_player[i].active &&
4172             !tape.player_participates[map_player_action[i]])
4173         {
4174           struct PlayerInfo *player = &stored_player[i];
4175           int jx = player->jx, jy = player->jy;
4176
4177 #if DEBUG_INIT_PLAYER
4178           Debug("game:init:player", "Removing player %d at (%d, %d)",
4179                 i + 1, jx, jy);
4180 #endif
4181
4182           player->active = FALSE;
4183           StorePlayer[jx][jy] = 0;
4184           Tile[jx][jy] = EL_EMPTY;
4185         }
4186       }
4187     }
4188
4189 #else
4190
4191     for (i = 0; i < MAX_PLAYERS; i++)
4192     {
4193       if (stored_player[i].active &&
4194           !tape.player_participates[i])
4195       {
4196         struct PlayerInfo *player = &stored_player[i];
4197         int jx = player->jx, jy = player->jy;
4198
4199         player->active = FALSE;
4200         StorePlayer[jx][jy] = 0;
4201         Tile[jx][jy] = EL_EMPTY;
4202       }
4203     }
4204 #endif
4205   }
4206   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4207   {
4208     // when in single player mode, eliminate all but the local player
4209
4210     for (i = 0; i < MAX_PLAYERS; i++)
4211     {
4212       struct PlayerInfo *player = &stored_player[i];
4213
4214       if (player->active && player != local_player)
4215       {
4216         int jx = player->jx, jy = player->jy;
4217
4218         player->active = FALSE;
4219         player->present = FALSE;
4220
4221         StorePlayer[jx][jy] = 0;
4222         Tile[jx][jy] = EL_EMPTY;
4223       }
4224     }
4225   }
4226
4227   for (i = 0; i < MAX_PLAYERS; i++)
4228     if (stored_player[i].active)
4229       game.players_still_needed++;
4230
4231   if (level.solved_by_one_player)
4232     game.players_still_needed = 1;
4233
4234   // when recording the game, store which players take part in the game
4235   if (tape.recording)
4236   {
4237 #if USE_NEW_PLAYER_ASSIGNMENTS
4238     for (i = 0; i < MAX_PLAYERS; i++)
4239       if (stored_player[i].connected)
4240         tape.player_participates[i] = TRUE;
4241 #else
4242     for (i = 0; i < MAX_PLAYERS; i++)
4243       if (stored_player[i].active)
4244         tape.player_participates[i] = TRUE;
4245 #endif
4246   }
4247
4248 #if DEBUG_INIT_PLAYER
4249   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4250 #endif
4251
4252   if (BorderElement == EL_EMPTY)
4253   {
4254     SBX_Left = 0;
4255     SBX_Right = lev_fieldx - SCR_FIELDX;
4256     SBY_Upper = 0;
4257     SBY_Lower = lev_fieldy - SCR_FIELDY;
4258   }
4259   else
4260   {
4261     SBX_Left = -1;
4262     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4263     SBY_Upper = -1;
4264     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4265   }
4266
4267   if (full_lev_fieldx <= SCR_FIELDX)
4268     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4269   if (full_lev_fieldy <= SCR_FIELDY)
4270     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4271
4272   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4273     SBX_Left--;
4274   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4275     SBY_Upper--;
4276
4277   // if local player not found, look for custom element that might create
4278   // the player (make some assumptions about the right custom element)
4279   if (!local_player->present)
4280   {
4281     int start_x = 0, start_y = 0;
4282     int found_rating = 0;
4283     int found_element = EL_UNDEFINED;
4284     int player_nr = local_player->index_nr;
4285
4286     SCAN_PLAYFIELD(x, y)
4287     {
4288       int element = Tile[x][y];
4289       int content;
4290       int xx, yy;
4291       boolean is_player;
4292
4293       if (level.use_start_element[player_nr] &&
4294           level.start_element[player_nr] == element &&
4295           found_rating < 4)
4296       {
4297         start_x = x;
4298         start_y = y;
4299
4300         found_rating = 4;
4301         found_element = element;
4302       }
4303
4304       if (!IS_CUSTOM_ELEMENT(element))
4305         continue;
4306
4307       if (CAN_CHANGE(element))
4308       {
4309         for (i = 0; i < element_info[element].num_change_pages; i++)
4310         {
4311           // check for player created from custom element as single target
4312           content = element_info[element].change_page[i].target_element;
4313           is_player = IS_PLAYER_ELEMENT(content);
4314
4315           if (is_player && (found_rating < 3 ||
4316                             (found_rating == 3 && element < found_element)))
4317           {
4318             start_x = x;
4319             start_y = y;
4320
4321             found_rating = 3;
4322             found_element = element;
4323           }
4324         }
4325       }
4326
4327       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4328       {
4329         // check for player created from custom element as explosion content
4330         content = element_info[element].content.e[xx][yy];
4331         is_player = IS_PLAYER_ELEMENT(content);
4332
4333         if (is_player && (found_rating < 2 ||
4334                           (found_rating == 2 && element < found_element)))
4335         {
4336           start_x = x + xx - 1;
4337           start_y = y + yy - 1;
4338
4339           found_rating = 2;
4340           found_element = element;
4341         }
4342
4343         if (!CAN_CHANGE(element))
4344           continue;
4345
4346         for (i = 0; i < element_info[element].num_change_pages; i++)
4347         {
4348           // check for player created from custom element as extended target
4349           content =
4350             element_info[element].change_page[i].target_content.e[xx][yy];
4351
4352           is_player = IS_PLAYER_ELEMENT(content);
4353
4354           if (is_player && (found_rating < 1 ||
4355                             (found_rating == 1 && element < found_element)))
4356           {
4357             start_x = x + xx - 1;
4358             start_y = y + yy - 1;
4359
4360             found_rating = 1;
4361             found_element = element;
4362           }
4363         }
4364       }
4365     }
4366
4367     scroll_x = SCROLL_POSITION_X(start_x);
4368     scroll_y = SCROLL_POSITION_Y(start_y);
4369   }
4370   else
4371   {
4372     scroll_x = SCROLL_POSITION_X(local_player->jx);
4373     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4374   }
4375
4376   // !!! FIX THIS (START) !!!
4377   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4378   {
4379     InitGameEngine_EM();
4380   }
4381   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4382   {
4383     InitGameEngine_SP();
4384   }
4385   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4386   {
4387     InitGameEngine_MM();
4388   }
4389   else
4390   {
4391     DrawLevel(REDRAW_FIELD);
4392     DrawAllPlayers();
4393
4394     // after drawing the level, correct some elements
4395     if (game.timegate_time_left == 0)
4396       CloseAllOpenTimegates();
4397   }
4398
4399   // blit playfield from scroll buffer to normal back buffer for fading in
4400   BlitScreenToBitmap(backbuffer);
4401   // !!! FIX THIS (END) !!!
4402
4403   DrawMaskedBorder(fade_mask);
4404
4405   FadeIn(fade_mask);
4406
4407 #if 1
4408   // full screen redraw is required at this point in the following cases:
4409   // - special editor door undrawn when game was started from level editor
4410   // - drawing area (playfield) was changed and has to be removed completely
4411   redraw_mask = REDRAW_ALL;
4412   BackToFront();
4413 #endif
4414
4415   if (!game.restart_level)
4416   {
4417     // copy default game door content to main double buffer
4418
4419     // !!! CHECK AGAIN !!!
4420     SetPanelBackground();
4421     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4422     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4423   }
4424
4425   SetPanelBackground();
4426   SetDrawBackgroundMask(REDRAW_DOOR_1);
4427
4428   UpdateAndDisplayGameControlValues();
4429
4430   if (!game.restart_level)
4431   {
4432     UnmapGameButtons();
4433     UnmapTapeButtons();
4434
4435     FreeGameButtons();
4436     CreateGameButtons();
4437
4438     MapGameButtons();
4439     MapTapeButtons();
4440
4441     // copy actual game door content to door double buffer for OpenDoor()
4442     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4443
4444     OpenDoor(DOOR_OPEN_ALL);
4445
4446     KeyboardAutoRepeatOffUnlessAutoplay();
4447
4448 #if DEBUG_INIT_PLAYER
4449     DebugPrintPlayerStatus("Player status (final)");
4450 #endif
4451   }
4452
4453   UnmapAllGadgets();
4454
4455   MapGameButtons();
4456   MapTapeButtons();
4457
4458   if (!game.restart_level && !tape.playing)
4459   {
4460     LevelStats_incPlayed(level_nr);
4461
4462     SaveLevelSetup_SeriesInfo();
4463   }
4464
4465   game.restart_level = FALSE;
4466   game.restart_game_message = NULL;
4467
4468   game.request_active = FALSE;
4469   game.request_active_or_moving = FALSE;
4470
4471   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4472     InitGameActions_MM();
4473
4474   SaveEngineSnapshotToListInitial();
4475
4476   if (!game.restart_level)
4477   {
4478     PlaySound(SND_GAME_STARTING);
4479
4480     if (setup.sound_music)
4481       PlayLevelMusic();
4482   }
4483
4484   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4485 }
4486
4487 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4488                         int actual_player_x, int actual_player_y)
4489 {
4490   // this is used for non-R'n'D game engines to update certain engine values
4491
4492   // needed to determine if sounds are played within the visible screen area
4493   scroll_x = actual_scroll_x;
4494   scroll_y = actual_scroll_y;
4495
4496   // needed to get player position for "follow finger" playing input method
4497   local_player->jx = actual_player_x;
4498   local_player->jy = actual_player_y;
4499 }
4500
4501 void InitMovDir(int x, int y)
4502 {
4503   int i, element = Tile[x][y];
4504   static int xy[4][2] =
4505   {
4506     {  0, +1 },
4507     { +1,  0 },
4508     {  0, -1 },
4509     { -1,  0 }
4510   };
4511   static int direction[3][4] =
4512   {
4513     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4514     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4515     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4516   };
4517
4518   switch (element)
4519   {
4520     case EL_BUG_RIGHT:
4521     case EL_BUG_UP:
4522     case EL_BUG_LEFT:
4523     case EL_BUG_DOWN:
4524       Tile[x][y] = EL_BUG;
4525       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4526       break;
4527
4528     case EL_SPACESHIP_RIGHT:
4529     case EL_SPACESHIP_UP:
4530     case EL_SPACESHIP_LEFT:
4531     case EL_SPACESHIP_DOWN:
4532       Tile[x][y] = EL_SPACESHIP;
4533       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4534       break;
4535
4536     case EL_BD_BUTTERFLY_RIGHT:
4537     case EL_BD_BUTTERFLY_UP:
4538     case EL_BD_BUTTERFLY_LEFT:
4539     case EL_BD_BUTTERFLY_DOWN:
4540       Tile[x][y] = EL_BD_BUTTERFLY;
4541       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4542       break;
4543
4544     case EL_BD_FIREFLY_RIGHT:
4545     case EL_BD_FIREFLY_UP:
4546     case EL_BD_FIREFLY_LEFT:
4547     case EL_BD_FIREFLY_DOWN:
4548       Tile[x][y] = EL_BD_FIREFLY;
4549       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4550       break;
4551
4552     case EL_PACMAN_RIGHT:
4553     case EL_PACMAN_UP:
4554     case EL_PACMAN_LEFT:
4555     case EL_PACMAN_DOWN:
4556       Tile[x][y] = EL_PACMAN;
4557       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4558       break;
4559
4560     case EL_YAMYAM_LEFT:
4561     case EL_YAMYAM_RIGHT:
4562     case EL_YAMYAM_UP:
4563     case EL_YAMYAM_DOWN:
4564       Tile[x][y] = EL_YAMYAM;
4565       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4566       break;
4567
4568     case EL_SP_SNIKSNAK:
4569       MovDir[x][y] = MV_UP;
4570       break;
4571
4572     case EL_SP_ELECTRON:
4573       MovDir[x][y] = MV_LEFT;
4574       break;
4575
4576     case EL_MOLE_LEFT:
4577     case EL_MOLE_RIGHT:
4578     case EL_MOLE_UP:
4579     case EL_MOLE_DOWN:
4580       Tile[x][y] = EL_MOLE;
4581       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4582       break;
4583
4584     case EL_SPRING_LEFT:
4585     case EL_SPRING_RIGHT:
4586       Tile[x][y] = EL_SPRING;
4587       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4588       break;
4589
4590     default:
4591       if (IS_CUSTOM_ELEMENT(element))
4592       {
4593         struct ElementInfo *ei = &element_info[element];
4594         int move_direction_initial = ei->move_direction_initial;
4595         int move_pattern = ei->move_pattern;
4596
4597         if (move_direction_initial == MV_START_PREVIOUS)
4598         {
4599           if (MovDir[x][y] != MV_NONE)
4600             return;
4601
4602           move_direction_initial = MV_START_AUTOMATIC;
4603         }
4604
4605         if (move_direction_initial == MV_START_RANDOM)
4606           MovDir[x][y] = 1 << RND(4);
4607         else if (move_direction_initial & MV_ANY_DIRECTION)
4608           MovDir[x][y] = move_direction_initial;
4609         else if (move_pattern == MV_ALL_DIRECTIONS ||
4610                  move_pattern == MV_TURNING_LEFT ||
4611                  move_pattern == MV_TURNING_RIGHT ||
4612                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4613                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4614                  move_pattern == MV_TURNING_RANDOM)
4615           MovDir[x][y] = 1 << RND(4);
4616         else if (move_pattern == MV_HORIZONTAL)
4617           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4618         else if (move_pattern == MV_VERTICAL)
4619           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4620         else if (move_pattern & MV_ANY_DIRECTION)
4621           MovDir[x][y] = element_info[element].move_pattern;
4622         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4623                  move_pattern == MV_ALONG_RIGHT_SIDE)
4624         {
4625           // use random direction as default start direction
4626           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4627             MovDir[x][y] = 1 << RND(4);
4628
4629           for (i = 0; i < NUM_DIRECTIONS; i++)
4630           {
4631             int x1 = x + xy[i][0];
4632             int y1 = y + xy[i][1];
4633
4634             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4635             {
4636               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4637                 MovDir[x][y] = direction[0][i];
4638               else
4639                 MovDir[x][y] = direction[1][i];
4640
4641               break;
4642             }
4643           }
4644         }                
4645       }
4646       else
4647       {
4648         MovDir[x][y] = 1 << RND(4);
4649
4650         if (element != EL_BUG &&
4651             element != EL_SPACESHIP &&
4652             element != EL_BD_BUTTERFLY &&
4653             element != EL_BD_FIREFLY)
4654           break;
4655
4656         for (i = 0; i < NUM_DIRECTIONS; i++)
4657         {
4658           int x1 = x + xy[i][0];
4659           int y1 = y + xy[i][1];
4660
4661           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4662           {
4663             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4664             {
4665               MovDir[x][y] = direction[0][i];
4666               break;
4667             }
4668             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4669                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4670             {
4671               MovDir[x][y] = direction[1][i];
4672               break;
4673             }
4674           }
4675         }
4676       }
4677       break;
4678   }
4679
4680   GfxDir[x][y] = MovDir[x][y];
4681 }
4682
4683 void InitAmoebaNr(int x, int y)
4684 {
4685   int i;
4686   int group_nr = AmoebaNeighbourNr(x, y);
4687
4688   if (group_nr == 0)
4689   {
4690     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4691     {
4692       if (AmoebaCnt[i] == 0)
4693       {
4694         group_nr = i;
4695         break;
4696       }
4697     }
4698   }
4699
4700   AmoebaNr[x][y] = group_nr;
4701   AmoebaCnt[group_nr]++;
4702   AmoebaCnt2[group_nr]++;
4703 }
4704
4705 static void LevelSolved_SetFinalGameValues(void)
4706 {
4707   game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4708   game.score_time_final = (level.use_step_counter ? TimePlayed :
4709                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4710
4711   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4712                       game_em.lev->score :
4713                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4714                       game_mm.score :
4715                       game.score);
4716
4717   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4718                        MM_HEALTH(game_mm.laser_overload_value) :
4719                        game.health);
4720
4721   game.LevelSolved_CountingTime = game.time_final;
4722   game.LevelSolved_CountingScore = game.score_final;
4723   game.LevelSolved_CountingHealth = game.health_final;
4724 }
4725
4726 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4727 {
4728   game.LevelSolved_CountingTime = time;
4729   game.LevelSolved_CountingScore = score;
4730   game.LevelSolved_CountingHealth = health;
4731
4732   game_panel_controls[GAME_PANEL_TIME].value = time;
4733   game_panel_controls[GAME_PANEL_SCORE].value = score;
4734   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4735
4736   DisplayGameControlValues();
4737 }
4738
4739 static void LevelSolved(void)
4740 {
4741   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4742       game.players_still_needed > 0)
4743     return;
4744
4745   game.LevelSolved = TRUE;
4746   game.GameOver = TRUE;
4747
4748   // needed here to display correct panel values while player walks into exit
4749   LevelSolved_SetFinalGameValues();
4750 }
4751
4752 void GameWon(void)
4753 {
4754   static int time_count_steps;
4755   static int time, time_final;
4756   static float score, score_final; // needed for time score < 10 for 10 seconds
4757   static int health, health_final;
4758   static int game_over_delay_1 = 0;
4759   static int game_over_delay_2 = 0;
4760   static int game_over_delay_3 = 0;
4761   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4762   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4763
4764   if (!game.LevelSolved_GameWon)
4765   {
4766     int i;
4767
4768     // do not start end game actions before the player stops moving (to exit)
4769     if (local_player->active && local_player->MovPos)
4770       return;
4771
4772     // calculate final game values after player finished walking into exit
4773     LevelSolved_SetFinalGameValues();
4774
4775     game.LevelSolved_GameWon = TRUE;
4776     game.LevelSolved_SaveTape = tape.recording;
4777     game.LevelSolved_SaveScore = !tape.playing;
4778
4779     if (!tape.playing)
4780     {
4781       LevelStats_incSolved(level_nr);
4782
4783       SaveLevelSetup_SeriesInfo();
4784     }
4785
4786     if (tape.auto_play)         // tape might already be stopped here
4787       tape.auto_play_level_solved = TRUE;
4788
4789     TapeStop();
4790
4791     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4792     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4793     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4794
4795     time = time_final = game.time_final;
4796     score = score_final = game.score_final;
4797     health = health_final = game.health_final;
4798
4799     // update game panel values before (delayed) counting of score (if any)
4800     LevelSolved_DisplayFinalGameValues(time, score, health);
4801
4802     // if level has time score defined, calculate new final game values
4803     if (time_score > 0)
4804     {
4805       int time_final_max = 999;
4806       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4807       int time_frames = 0;
4808       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4809       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4810
4811       if (TimeLeft > 0)
4812       {
4813         time_final = 0;
4814         time_frames = time_frames_left;
4815       }
4816       else if (game.no_time_limit && TimePlayed < time_final_max)
4817       {
4818         time_final = time_final_max;
4819         time_frames = time_frames_final_max - time_frames_played;
4820       }
4821
4822       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4823
4824       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4825
4826       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4827       {
4828         health_final = 0;
4829         score_final += health * time_score;
4830       }
4831
4832       game.score_final = score_final;
4833       game.health_final = health_final;
4834     }
4835
4836     // if not counting score after game, immediately update game panel values
4837     if (level_editor_test_game || !setup.count_score_after_game)
4838     {
4839       time = time_final;
4840       score = score_final;
4841
4842       LevelSolved_DisplayFinalGameValues(time, score, health);
4843     }
4844
4845     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4846     {
4847       // check if last player has left the level
4848       if (game.exit_x >= 0 &&
4849           game.exit_y >= 0)
4850       {
4851         int x = game.exit_x;
4852         int y = game.exit_y;
4853         int element = Tile[x][y];
4854
4855         // close exit door after last player
4856         if ((game.all_players_gone &&
4857              (element == EL_EXIT_OPEN ||
4858               element == EL_SP_EXIT_OPEN ||
4859               element == EL_STEEL_EXIT_OPEN)) ||
4860             element == EL_EM_EXIT_OPEN ||
4861             element == EL_EM_STEEL_EXIT_OPEN)
4862         {
4863
4864           Tile[x][y] =
4865             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4866              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4867              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4868              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4869              EL_EM_STEEL_EXIT_CLOSING);
4870
4871           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4872         }
4873
4874         // player disappears
4875         DrawLevelField(x, y);
4876       }
4877
4878       for (i = 0; i < MAX_PLAYERS; i++)
4879       {
4880         struct PlayerInfo *player = &stored_player[i];
4881
4882         if (player->present)
4883         {
4884           RemovePlayer(player);
4885
4886           // player disappears
4887           DrawLevelField(player->jx, player->jy);
4888         }
4889       }
4890     }
4891
4892     PlaySound(SND_GAME_WINNING);
4893   }
4894
4895   if (setup.count_score_after_game)
4896   {
4897     if (time != time_final)
4898     {
4899       if (game_over_delay_1 > 0)
4900       {
4901         game_over_delay_1--;
4902
4903         return;
4904       }
4905
4906       int time_to_go = ABS(time_final - time);
4907       int time_count_dir = (time < time_final ? +1 : -1);
4908
4909       if (time_to_go < time_count_steps)
4910         time_count_steps = 1;
4911
4912       time  += time_count_steps * time_count_dir;
4913       score += time_count_steps * time_score;
4914
4915       // set final score to correct rounding differences after counting score
4916       if (time == time_final)
4917         score = score_final;
4918
4919       LevelSolved_DisplayFinalGameValues(time, score, health);
4920
4921       if (time == time_final)
4922         StopSound(SND_GAME_LEVELTIME_BONUS);
4923       else if (setup.sound_loops)
4924         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4925       else
4926         PlaySound(SND_GAME_LEVELTIME_BONUS);
4927
4928       return;
4929     }
4930
4931     if (health != health_final)
4932     {
4933       if (game_over_delay_2 > 0)
4934       {
4935         game_over_delay_2--;
4936
4937         return;
4938       }
4939
4940       int health_count_dir = (health < health_final ? +1 : -1);
4941
4942       health += health_count_dir;
4943       score  += time_score;
4944
4945       LevelSolved_DisplayFinalGameValues(time, score, health);
4946
4947       if (health == health_final)
4948         StopSound(SND_GAME_LEVELTIME_BONUS);
4949       else if (setup.sound_loops)
4950         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4951       else
4952         PlaySound(SND_GAME_LEVELTIME_BONUS);
4953
4954       return;
4955     }
4956   }
4957
4958   game.panel.active = FALSE;
4959
4960   if (game_over_delay_3 > 0)
4961   {
4962     game_over_delay_3--;
4963
4964     return;
4965   }
4966
4967   GameEnd();
4968 }
4969
4970 void GameEnd(void)
4971 {
4972   // used instead of "level_nr" (needed for network games)
4973   int last_level_nr = levelset.level_nr;
4974   boolean tape_saved = FALSE;
4975
4976   game.LevelSolved_GameEnd = TRUE;
4977
4978   if (game.LevelSolved_SaveTape)
4979   {
4980     // make sure that request dialog to save tape does not open door again
4981     if (!global.use_envelope_request)
4982       CloseDoor(DOOR_CLOSE_1);
4983
4984     // ask to save tape
4985     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
4986
4987     // set unique basename for score tape (also saved in high score table)
4988     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
4989   }
4990
4991   // if no tape is to be saved, close both doors simultaneously
4992   CloseDoor(DOOR_CLOSE_ALL);
4993
4994   if (level_editor_test_game)
4995   {
4996     SetGameStatus(GAME_MODE_MAIN);
4997
4998     DrawMainMenu();
4999
5000     return;
5001   }
5002
5003   if (!game.LevelSolved_SaveScore)
5004   {
5005     SetGameStatus(GAME_MODE_MAIN);
5006
5007     DrawMainMenu();
5008
5009     return;
5010   }
5011
5012   if (level_nr == leveldir_current->handicap_level)
5013   {
5014     leveldir_current->handicap_level++;
5015
5016     SaveLevelSetup_SeriesInfo();
5017   }
5018
5019   // save score and score tape before potentially erasing tape below
5020   NewHighScore(last_level_nr, tape_saved);
5021
5022   if (setup.increment_levels &&
5023       level_nr < leveldir_current->last_level &&
5024       !network_playing)
5025   {
5026     level_nr++;         // advance to next level
5027     TapeErase();        // start with empty tape
5028
5029     if (setup.auto_play_next_level)
5030     {
5031       LoadLevel(level_nr);
5032
5033       SaveLevelSetup_SeriesInfo();
5034     }
5035   }
5036
5037   if (scores.last_added >= 0 && setup.show_scores_after_game)
5038   {
5039     SetGameStatus(GAME_MODE_SCORES);
5040
5041     DrawHallOfFame(last_level_nr);
5042   }
5043   else if (setup.auto_play_next_level && setup.increment_levels &&
5044            last_level_nr < leveldir_current->last_level &&
5045            !network_playing)
5046   {
5047     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5048   }
5049   else
5050   {
5051     SetGameStatus(GAME_MODE_MAIN);
5052
5053     DrawMainMenu();
5054   }
5055 }
5056
5057 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5058                          boolean one_score_entry_per_name)
5059 {
5060   int i;
5061
5062   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5063     return -1;
5064
5065   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5066   {
5067     struct ScoreEntry *entry = &list->entry[i];
5068     boolean score_is_better = (new_entry->score >  entry->score);
5069     boolean score_is_equal  = (new_entry->score == entry->score);
5070     boolean time_is_better  = (new_entry->time  <  entry->time);
5071     boolean time_is_equal   = (new_entry->time  == entry->time);
5072     boolean better_by_score = (score_is_better ||
5073                                (score_is_equal && time_is_better));
5074     boolean better_by_time  = (time_is_better ||
5075                                (time_is_equal && score_is_better));
5076     boolean is_better = (level.rate_time_over_score ? better_by_time :
5077                          better_by_score);
5078     boolean entry_is_empty = (entry->score == 0 &&
5079                               entry->time == 0);
5080
5081     // prevent adding server score entries if also existing in local score file
5082     // (special case: historic score entries have an empty tape basename entry)
5083     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5084         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5085       return -1;
5086
5087     if (is_better || entry_is_empty)
5088     {
5089       // player has made it to the hall of fame
5090
5091       if (i < MAX_SCORE_ENTRIES - 1)
5092       {
5093         int m = MAX_SCORE_ENTRIES - 1;
5094         int l;
5095
5096         if (one_score_entry_per_name)
5097         {
5098           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5099             if (strEqual(list->entry[l].name, new_entry->name))
5100               m = l;
5101
5102           if (m == i)   // player's new highscore overwrites his old one
5103             goto put_into_list;
5104         }
5105
5106         for (l = m; l > i; l--)
5107           list->entry[l] = list->entry[l - 1];
5108       }
5109
5110       put_into_list:
5111
5112       *entry = *new_entry;
5113
5114       return i;
5115     }
5116     else if (one_score_entry_per_name &&
5117              strEqual(entry->name, new_entry->name))
5118     {
5119       // player already in high score list with better score or time
5120
5121       return -1;
5122     }
5123   }
5124
5125   return -1;
5126 }
5127
5128 void NewHighScore(int level_nr, boolean tape_saved)
5129 {
5130   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5131   boolean one_per_name = FALSE;
5132
5133   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5134   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5135
5136   new_entry.score = game.score_final;
5137   new_entry.time = game.score_time_final;
5138
5139   LoadScore(level_nr);
5140
5141   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5142
5143   if (scores.last_added < 0)
5144     return;
5145
5146   SaveScore(level_nr);
5147
5148   // store last added local score entry (before merging server scores)
5149   scores.last_added_local = scores.last_added;
5150
5151   if (!game.LevelSolved_SaveTape)
5152     return;
5153
5154   SaveScoreTape(level_nr);
5155
5156   if (setup.ask_for_using_api_server)
5157   {
5158     setup.use_api_server =
5159       Request("Upload your score and tape to the high score server?", REQ_ASK);
5160
5161     if (!setup.use_api_server)
5162       Request("Not using high score server! Use setup menu to enable again!",
5163               REQ_CONFIRM);
5164
5165     runtime.use_api_server = setup.use_api_server;
5166
5167     // after asking for using API server once, do not ask again
5168     setup.ask_for_using_api_server = FALSE;
5169
5170     SaveSetup_ServerSetup();
5171   }
5172
5173   SaveServerScore(level_nr, tape_saved);
5174 }
5175
5176 void MergeServerScore(void)
5177 {
5178   struct ScoreEntry last_added_entry;
5179   boolean one_per_name = FALSE;
5180   int i;
5181
5182   if (scores.last_added >= 0)
5183     last_added_entry = scores.entry[scores.last_added];
5184
5185   for (i = 0; i < server_scores.num_entries; i++)
5186   {
5187     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5188
5189     if (pos >= 0 && pos <= scores.last_added)
5190       scores.last_added++;
5191   }
5192
5193   if (scores.last_added >= MAX_SCORE_ENTRIES)
5194   {
5195     scores.last_added = MAX_SCORE_ENTRIES - 1;
5196     scores.force_last_added = TRUE;
5197
5198     scores.entry[scores.last_added] = last_added_entry;
5199   }
5200 }
5201
5202 static int getElementMoveStepsizeExt(int x, int y, int direction)
5203 {
5204   int element = Tile[x][y];
5205   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5206   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5207   int horiz_move = (dx != 0);
5208   int sign = (horiz_move ? dx : dy);
5209   int step = sign * element_info[element].move_stepsize;
5210
5211   // special values for move stepsize for spring and things on conveyor belt
5212   if (horiz_move)
5213   {
5214     if (CAN_FALL(element) &&
5215         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5216       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5217     else if (element == EL_SPRING)
5218       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5219   }
5220
5221   return step;
5222 }
5223
5224 static int getElementMoveStepsize(int x, int y)
5225 {
5226   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5227 }
5228
5229 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5230 {
5231   if (player->GfxAction != action || player->GfxDir != dir)
5232   {
5233     player->GfxAction = action;
5234     player->GfxDir = dir;
5235     player->Frame = 0;
5236     player->StepFrame = 0;
5237   }
5238 }
5239
5240 static void ResetGfxFrame(int x, int y)
5241 {
5242   // profiling showed that "autotest" spends 10~20% of its time in this function
5243   if (DrawingDeactivatedField())
5244     return;
5245
5246   int element = Tile[x][y];
5247   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5248
5249   if (graphic_info[graphic].anim_global_sync)
5250     GfxFrame[x][y] = FrameCounter;
5251   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5252     GfxFrame[x][y] = CustomValue[x][y];
5253   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5254     GfxFrame[x][y] = element_info[element].collect_score;
5255   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5256     GfxFrame[x][y] = ChangeDelay[x][y];
5257 }
5258
5259 static void ResetGfxAnimation(int x, int y)
5260 {
5261   GfxAction[x][y] = ACTION_DEFAULT;
5262   GfxDir[x][y] = MovDir[x][y];
5263   GfxFrame[x][y] = 0;
5264
5265   ResetGfxFrame(x, y);
5266 }
5267
5268 static void ResetRandomAnimationValue(int x, int y)
5269 {
5270   GfxRandom[x][y] = INIT_GFX_RANDOM();
5271 }
5272
5273 static void InitMovingField(int x, int y, int direction)
5274 {
5275   int element = Tile[x][y];
5276   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5277   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5278   int newx = x + dx;
5279   int newy = y + dy;
5280   boolean is_moving_before, is_moving_after;
5281
5282   // check if element was/is moving or being moved before/after mode change
5283   is_moving_before = (WasJustMoving[x][y] != 0);
5284   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5285
5286   // reset animation only for moving elements which change direction of moving
5287   // or which just started or stopped moving
5288   // (else CEs with property "can move" / "not moving" are reset each frame)
5289   if (is_moving_before != is_moving_after ||
5290       direction != MovDir[x][y])
5291     ResetGfxAnimation(x, y);
5292
5293   MovDir[x][y] = direction;
5294   GfxDir[x][y] = direction;
5295
5296   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5297                      direction == MV_DOWN && CAN_FALL(element) ?
5298                      ACTION_FALLING : ACTION_MOVING);
5299
5300   // this is needed for CEs with property "can move" / "not moving"
5301
5302   if (is_moving_after)
5303   {
5304     if (Tile[newx][newy] == EL_EMPTY)
5305       Tile[newx][newy] = EL_BLOCKED;
5306
5307     MovDir[newx][newy] = MovDir[x][y];
5308
5309     CustomValue[newx][newy] = CustomValue[x][y];
5310
5311     GfxFrame[newx][newy] = GfxFrame[x][y];
5312     GfxRandom[newx][newy] = GfxRandom[x][y];
5313     GfxAction[newx][newy] = GfxAction[x][y];
5314     GfxDir[newx][newy] = GfxDir[x][y];
5315   }
5316 }
5317
5318 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5319 {
5320   int direction = MovDir[x][y];
5321   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5322   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5323
5324   *goes_to_x = newx;
5325   *goes_to_y = newy;
5326 }
5327
5328 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5329 {
5330   int oldx = x, oldy = y;
5331   int direction = MovDir[x][y];
5332
5333   if (direction == MV_LEFT)
5334     oldx++;
5335   else if (direction == MV_RIGHT)
5336     oldx--;
5337   else if (direction == MV_UP)
5338     oldy++;
5339   else if (direction == MV_DOWN)
5340     oldy--;
5341
5342   *comes_from_x = oldx;
5343   *comes_from_y = oldy;
5344 }
5345
5346 static int MovingOrBlocked2Element(int x, int y)
5347 {
5348   int element = Tile[x][y];
5349
5350   if (element == EL_BLOCKED)
5351   {
5352     int oldx, oldy;
5353
5354     Blocked2Moving(x, y, &oldx, &oldy);
5355     return Tile[oldx][oldy];
5356   }
5357   else
5358     return element;
5359 }
5360
5361 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5362 {
5363   // like MovingOrBlocked2Element(), but if element is moving
5364   // and (x,y) is the field the moving element is just leaving,
5365   // return EL_BLOCKED instead of the element value
5366   int element = Tile[x][y];
5367
5368   if (IS_MOVING(x, y))
5369   {
5370     if (element == EL_BLOCKED)
5371     {
5372       int oldx, oldy;
5373
5374       Blocked2Moving(x, y, &oldx, &oldy);
5375       return Tile[oldx][oldy];
5376     }
5377     else
5378       return EL_BLOCKED;
5379   }
5380   else
5381     return element;
5382 }
5383
5384 static void RemoveField(int x, int y)
5385 {
5386   Tile[x][y] = EL_EMPTY;
5387
5388   MovPos[x][y] = 0;
5389   MovDir[x][y] = 0;
5390   MovDelay[x][y] = 0;
5391
5392   CustomValue[x][y] = 0;
5393
5394   AmoebaNr[x][y] = 0;
5395   ChangeDelay[x][y] = 0;
5396   ChangePage[x][y] = -1;
5397   Pushed[x][y] = FALSE;
5398
5399   GfxElement[x][y] = EL_UNDEFINED;
5400   GfxAction[x][y] = ACTION_DEFAULT;
5401   GfxDir[x][y] = MV_NONE;
5402 }
5403
5404 static void RemoveMovingField(int x, int y)
5405 {
5406   int oldx = x, oldy = y, newx = x, newy = y;
5407   int element = Tile[x][y];
5408   int next_element = EL_UNDEFINED;
5409
5410   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5411     return;
5412
5413   if (IS_MOVING(x, y))
5414   {
5415     Moving2Blocked(x, y, &newx, &newy);
5416
5417     if (Tile[newx][newy] != EL_BLOCKED)
5418     {
5419       // element is moving, but target field is not free (blocked), but
5420       // already occupied by something different (example: acid pool);
5421       // in this case, only remove the moving field, but not the target
5422
5423       RemoveField(oldx, oldy);
5424
5425       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5426
5427       TEST_DrawLevelField(oldx, oldy);
5428
5429       return;
5430     }
5431   }
5432   else if (element == EL_BLOCKED)
5433   {
5434     Blocked2Moving(x, y, &oldx, &oldy);
5435     if (!IS_MOVING(oldx, oldy))
5436       return;
5437   }
5438
5439   if (element == EL_BLOCKED &&
5440       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5441        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5442        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5443        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5444        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5445        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5446     next_element = get_next_element(Tile[oldx][oldy]);
5447
5448   RemoveField(oldx, oldy);
5449   RemoveField(newx, newy);
5450
5451   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5452
5453   if (next_element != EL_UNDEFINED)
5454     Tile[oldx][oldy] = next_element;
5455
5456   TEST_DrawLevelField(oldx, oldy);
5457   TEST_DrawLevelField(newx, newy);
5458 }
5459
5460 void DrawDynamite(int x, int y)
5461 {
5462   int sx = SCREENX(x), sy = SCREENY(y);
5463   int graphic = el2img(Tile[x][y]);
5464   int frame;
5465
5466   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5467     return;
5468
5469   if (IS_WALKABLE_INSIDE(Back[x][y]))
5470     return;
5471
5472   if (Back[x][y])
5473     DrawLevelElement(x, y, Back[x][y]);
5474   else if (Store[x][y])
5475     DrawLevelElement(x, y, Store[x][y]);
5476   else if (game.use_masked_elements)
5477     DrawLevelElement(x, y, EL_EMPTY);
5478
5479   frame = getGraphicAnimationFrameXY(graphic, x, y);
5480
5481   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5482     DrawGraphicThruMask(sx, sy, graphic, frame);
5483   else
5484     DrawGraphic(sx, sy, graphic, frame);
5485 }
5486
5487 static void CheckDynamite(int x, int y)
5488 {
5489   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5490   {
5491     MovDelay[x][y]--;
5492
5493     if (MovDelay[x][y] != 0)
5494     {
5495       DrawDynamite(x, y);
5496       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5497
5498       return;
5499     }
5500   }
5501
5502   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5503
5504   Bang(x, y);
5505 }
5506
5507 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5508 {
5509   boolean num_checked_players = 0;
5510   int i;
5511
5512   for (i = 0; i < MAX_PLAYERS; i++)
5513   {
5514     if (stored_player[i].active)
5515     {
5516       int sx = stored_player[i].jx;
5517       int sy = stored_player[i].jy;
5518
5519       if (num_checked_players == 0)
5520       {
5521         *sx1 = *sx2 = sx;
5522         *sy1 = *sy2 = sy;
5523       }
5524       else
5525       {
5526         *sx1 = MIN(*sx1, sx);
5527         *sy1 = MIN(*sy1, sy);
5528         *sx2 = MAX(*sx2, sx);
5529         *sy2 = MAX(*sy2, sy);
5530       }
5531
5532       num_checked_players++;
5533     }
5534   }
5535 }
5536
5537 static boolean checkIfAllPlayersFitToScreen_RND(void)
5538 {
5539   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5540
5541   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5542
5543   return (sx2 - sx1 < SCR_FIELDX &&
5544           sy2 - sy1 < SCR_FIELDY);
5545 }
5546
5547 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5548 {
5549   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5550
5551   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5552
5553   *sx = (sx1 + sx2) / 2;
5554   *sy = (sy1 + sy2) / 2;
5555 }
5556
5557 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5558                                boolean center_screen, boolean quick_relocation)
5559 {
5560   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5561   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5562   boolean no_delay = (tape.warp_forward);
5563   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5564   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5565   int new_scroll_x, new_scroll_y;
5566
5567   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5568   {
5569     // case 1: quick relocation inside visible screen (without scrolling)
5570
5571     RedrawPlayfield();
5572
5573     return;
5574   }
5575
5576   if (!level.shifted_relocation || center_screen)
5577   {
5578     // relocation _with_ centering of screen
5579
5580     new_scroll_x = SCROLL_POSITION_X(x);
5581     new_scroll_y = SCROLL_POSITION_Y(y);
5582   }
5583   else
5584   {
5585     // relocation _without_ centering of screen
5586
5587     int center_scroll_x = SCROLL_POSITION_X(old_x);
5588     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5589     int offset_x = x + (scroll_x - center_scroll_x);
5590     int offset_y = y + (scroll_y - center_scroll_y);
5591
5592     // for new screen position, apply previous offset to center position
5593     new_scroll_x = SCROLL_POSITION_X(offset_x);
5594     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5595   }
5596
5597   if (quick_relocation)
5598   {
5599     // case 2: quick relocation (redraw without visible scrolling)
5600
5601     scroll_x = new_scroll_x;
5602     scroll_y = new_scroll_y;
5603
5604     RedrawPlayfield();
5605
5606     return;
5607   }
5608
5609   // case 3: visible relocation (with scrolling to new position)
5610
5611   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5612
5613   SetVideoFrameDelay(wait_delay_value);
5614
5615   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5616   {
5617     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5618     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5619
5620     if (dx == 0 && dy == 0)             // no scrolling needed at all
5621       break;
5622
5623     scroll_x -= dx;
5624     scroll_y -= dy;
5625
5626     // set values for horizontal/vertical screen scrolling (half tile size)
5627     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5628     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5629     int pos_x = dx * TILEX / 2;
5630     int pos_y = dy * TILEY / 2;
5631     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5632     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5633
5634     ScrollLevel(dx, dy);
5635     DrawAllPlayers();
5636
5637     // scroll in two steps of half tile size to make things smoother
5638     BlitScreenToBitmapExt_RND(window, fx, fy);
5639
5640     // scroll second step to align at full tile size
5641     BlitScreenToBitmap(window);
5642   }
5643
5644   DrawAllPlayers();
5645   BackToFront();
5646
5647   SetVideoFrameDelay(frame_delay_value_old);
5648 }
5649
5650 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5651 {
5652   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5653   int player_nr = GET_PLAYER_NR(el_player);
5654   struct PlayerInfo *player = &stored_player[player_nr];
5655   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5656   boolean no_delay = (tape.warp_forward);
5657   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5658   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5659   int old_jx = player->jx;
5660   int old_jy = player->jy;
5661   int old_element = Tile[old_jx][old_jy];
5662   int element = Tile[jx][jy];
5663   boolean player_relocated = (old_jx != jx || old_jy != jy);
5664
5665   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5666   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5667   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5668   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5669   int leave_side_horiz = move_dir_horiz;
5670   int leave_side_vert  = move_dir_vert;
5671   int enter_side = enter_side_horiz | enter_side_vert;
5672   int leave_side = leave_side_horiz | leave_side_vert;
5673
5674   if (player->buried)           // do not reanimate dead player
5675     return;
5676
5677   if (!player_relocated)        // no need to relocate the player
5678     return;
5679
5680   if (IS_PLAYER(jx, jy))        // player already placed at new position
5681   {
5682     RemoveField(jx, jy);        // temporarily remove newly placed player
5683     DrawLevelField(jx, jy);
5684   }
5685
5686   if (player->present)
5687   {
5688     while (player->MovPos)
5689     {
5690       ScrollPlayer(player, SCROLL_GO_ON);
5691       ScrollScreen(NULL, SCROLL_GO_ON);
5692
5693       AdvanceFrameAndPlayerCounters(player->index_nr);
5694
5695       DrawPlayer(player);
5696
5697       BackToFront_WithFrameDelay(wait_delay_value);
5698     }
5699
5700     DrawPlayer(player);         // needed here only to cleanup last field
5701     DrawLevelField(player->jx, player->jy);     // remove player graphic
5702
5703     player->is_moving = FALSE;
5704   }
5705
5706   if (IS_CUSTOM_ELEMENT(old_element))
5707     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5708                                CE_LEFT_BY_PLAYER,
5709                                player->index_bit, leave_side);
5710
5711   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5712                                       CE_PLAYER_LEAVES_X,
5713                                       player->index_bit, leave_side);
5714
5715   Tile[jx][jy] = el_player;
5716   InitPlayerField(jx, jy, el_player, TRUE);
5717
5718   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5719      possible that the relocation target field did not contain a player element,
5720      but a walkable element, to which the new player was relocated -- in this
5721      case, restore that (already initialized!) element on the player field */
5722   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5723   {
5724     Tile[jx][jy] = element;     // restore previously existing element
5725   }
5726
5727   // only visually relocate centered player
5728   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5729                      FALSE, level.instant_relocation);
5730
5731   TestIfPlayerTouchesBadThing(jx, jy);
5732   TestIfPlayerTouchesCustomElement(jx, jy);
5733
5734   if (IS_CUSTOM_ELEMENT(element))
5735     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5736                                player->index_bit, enter_side);
5737
5738   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5739                                       player->index_bit, enter_side);
5740
5741   if (player->is_switching)
5742   {
5743     /* ensure that relocation while still switching an element does not cause
5744        a new element to be treated as also switched directly after relocation
5745        (this is important for teleporter switches that teleport the player to
5746        a place where another teleporter switch is in the same direction, which
5747        would then incorrectly be treated as immediately switched before the
5748        direction key that caused the switch was released) */
5749
5750     player->switch_x += jx - old_jx;
5751     player->switch_y += jy - old_jy;
5752   }
5753 }
5754
5755 static void Explode(int ex, int ey, int phase, int mode)
5756 {
5757   int x, y;
5758   int last_phase;
5759   int border_element;
5760
5761   // !!! eliminate this variable !!!
5762   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5763
5764   if (game.explosions_delayed)
5765   {
5766     ExplodeField[ex][ey] = mode;
5767     return;
5768   }
5769
5770   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5771   {
5772     int center_element = Tile[ex][ey];
5773     int artwork_element, explosion_element;     // set these values later
5774
5775     // remove things displayed in background while burning dynamite
5776     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5777       Back[ex][ey] = 0;
5778
5779     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5780     {
5781       // put moving element to center field (and let it explode there)
5782       center_element = MovingOrBlocked2Element(ex, ey);
5783       RemoveMovingField(ex, ey);
5784       Tile[ex][ey] = center_element;
5785     }
5786
5787     // now "center_element" is finally determined -- set related values now
5788     artwork_element = center_element;           // for custom player artwork
5789     explosion_element = center_element;         // for custom player artwork
5790
5791     if (IS_PLAYER(ex, ey))
5792     {
5793       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5794
5795       artwork_element = stored_player[player_nr].artwork_element;
5796
5797       if (level.use_explosion_element[player_nr])
5798       {
5799         explosion_element = level.explosion_element[player_nr];
5800         artwork_element = explosion_element;
5801       }
5802     }
5803
5804     if (mode == EX_TYPE_NORMAL ||
5805         mode == EX_TYPE_CENTER ||
5806         mode == EX_TYPE_CROSS)
5807       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5808
5809     last_phase = element_info[explosion_element].explosion_delay + 1;
5810
5811     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5812     {
5813       int xx = x - ex + 1;
5814       int yy = y - ey + 1;
5815       int element;
5816
5817       if (!IN_LEV_FIELD(x, y) ||
5818           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5819           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5820         continue;
5821
5822       element = Tile[x][y];
5823
5824       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5825       {
5826         element = MovingOrBlocked2Element(x, y);
5827
5828         if (!IS_EXPLOSION_PROOF(element))
5829           RemoveMovingField(x, y);
5830       }
5831
5832       // indestructible elements can only explode in center (but not flames)
5833       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5834                                            mode == EX_TYPE_BORDER)) ||
5835           element == EL_FLAMES)
5836         continue;
5837
5838       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5839          behaviour, for example when touching a yamyam that explodes to rocks
5840          with active deadly shield, a rock is created under the player !!! */
5841       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5842 #if 0
5843       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5844           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5845            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5846 #else
5847       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5848 #endif
5849       {
5850         if (IS_ACTIVE_BOMB(element))
5851         {
5852           // re-activate things under the bomb like gate or penguin
5853           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5854           Back[x][y] = 0;
5855         }
5856
5857         continue;
5858       }
5859
5860       // save walkable background elements while explosion on same tile
5861       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5862           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5863         Back[x][y] = element;
5864
5865       // ignite explodable elements reached by other explosion
5866       if (element == EL_EXPLOSION)
5867         element = Store2[x][y];
5868
5869       if (AmoebaNr[x][y] &&
5870           (element == EL_AMOEBA_FULL ||
5871            element == EL_BD_AMOEBA ||
5872            element == EL_AMOEBA_GROWING))
5873       {
5874         AmoebaCnt[AmoebaNr[x][y]]--;
5875         AmoebaCnt2[AmoebaNr[x][y]]--;
5876       }
5877
5878       RemoveField(x, y);
5879
5880       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5881       {
5882         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5883
5884         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5885
5886         if (PLAYERINFO(ex, ey)->use_murphy)
5887           Store[x][y] = EL_EMPTY;
5888       }
5889
5890       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5891       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5892       else if (IS_PLAYER_ELEMENT(center_element))
5893         Store[x][y] = EL_EMPTY;
5894       else if (center_element == EL_YAMYAM)
5895         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5896       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5897         Store[x][y] = element_info[center_element].content.e[xx][yy];
5898 #if 1
5899       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5900       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5901       // otherwise) -- FIX THIS !!!
5902       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5903         Store[x][y] = element_info[element].content.e[1][1];
5904 #else
5905       else if (!CAN_EXPLODE(element))
5906         Store[x][y] = element_info[element].content.e[1][1];
5907 #endif
5908       else
5909         Store[x][y] = EL_EMPTY;
5910
5911       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5912           center_element == EL_AMOEBA_TO_DIAMOND)
5913         Store2[x][y] = element;
5914
5915       Tile[x][y] = EL_EXPLOSION;
5916       GfxElement[x][y] = artwork_element;
5917
5918       ExplodePhase[x][y] = 1;
5919       ExplodeDelay[x][y] = last_phase;
5920
5921       Stop[x][y] = TRUE;
5922     }
5923
5924     if (center_element == EL_YAMYAM)
5925       game.yamyam_content_nr =
5926         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5927
5928     return;
5929   }
5930
5931   if (Stop[ex][ey])
5932     return;
5933
5934   x = ex;
5935   y = ey;
5936
5937   if (phase == 1)
5938     GfxFrame[x][y] = 0;         // restart explosion animation
5939
5940   last_phase = ExplodeDelay[x][y];
5941
5942   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5943
5944   // this can happen if the player leaves an explosion just in time
5945   if (GfxElement[x][y] == EL_UNDEFINED)
5946     GfxElement[x][y] = EL_EMPTY;
5947
5948   border_element = Store2[x][y];
5949   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5950     border_element = StorePlayer[x][y];
5951
5952   if (phase == element_info[border_element].ignition_delay ||
5953       phase == last_phase)
5954   {
5955     boolean border_explosion = FALSE;
5956
5957     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5958         !PLAYER_EXPLOSION_PROTECTED(x, y))
5959     {
5960       KillPlayerUnlessExplosionProtected(x, y);
5961       border_explosion = TRUE;
5962     }
5963     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5964     {
5965       Tile[x][y] = Store2[x][y];
5966       Store2[x][y] = 0;
5967       Bang(x, y);
5968       border_explosion = TRUE;
5969     }
5970     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5971     {
5972       AmoebaToDiamond(x, y);
5973       Store2[x][y] = 0;
5974       border_explosion = TRUE;
5975     }
5976
5977     // if an element just explodes due to another explosion (chain-reaction),
5978     // do not immediately end the new explosion when it was the last frame of
5979     // the explosion (as it would be done in the following "if"-statement!)
5980     if (border_explosion && phase == last_phase)
5981       return;
5982   }
5983
5984   if (phase == last_phase)
5985   {
5986     int element;
5987
5988     element = Tile[x][y] = Store[x][y];
5989     Store[x][y] = Store2[x][y] = 0;
5990     GfxElement[x][y] = EL_UNDEFINED;
5991
5992     // player can escape from explosions and might therefore be still alive
5993     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5994         element <= EL_PLAYER_IS_EXPLODING_4)
5995     {
5996       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5997       int explosion_element = EL_PLAYER_1 + player_nr;
5998       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5999       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6000
6001       if (level.use_explosion_element[player_nr])
6002         explosion_element = level.explosion_element[player_nr];
6003
6004       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6005                     element_info[explosion_element].content.e[xx][yy]);
6006     }
6007
6008     // restore probably existing indestructible background element
6009     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6010       element = Tile[x][y] = Back[x][y];
6011     Back[x][y] = 0;
6012
6013     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6014     GfxDir[x][y] = MV_NONE;
6015     ChangeDelay[x][y] = 0;
6016     ChangePage[x][y] = -1;
6017
6018     CustomValue[x][y] = 0;
6019
6020     InitField_WithBug2(x, y, FALSE);
6021
6022     TEST_DrawLevelField(x, y);
6023
6024     TestIfElementTouchesCustomElement(x, y);
6025
6026     if (GFX_CRUMBLED(element))
6027       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6028
6029     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6030       StorePlayer[x][y] = 0;
6031
6032     if (IS_PLAYER_ELEMENT(element))
6033       RelocatePlayer(x, y, element);
6034   }
6035   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6036   {
6037     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6038     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6039
6040     if (phase == delay)
6041       TEST_DrawLevelFieldCrumbled(x, y);
6042
6043     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6044     {
6045       DrawLevelElement(x, y, Back[x][y]);
6046       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6047     }
6048     else if (IS_WALKABLE_UNDER(Back[x][y]))
6049     {
6050       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6051       DrawLevelElementThruMask(x, y, Back[x][y]);
6052     }
6053     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6054       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6055   }
6056 }
6057
6058 static void DynaExplode(int ex, int ey)
6059 {
6060   int i, j;
6061   int dynabomb_element = Tile[ex][ey];
6062   int dynabomb_size = 1;
6063   boolean dynabomb_xl = FALSE;
6064   struct PlayerInfo *player;
6065   static int xy[4][2] =
6066   {
6067     { 0, -1 },
6068     { -1, 0 },
6069     { +1, 0 },
6070     { 0, +1 }
6071   };
6072
6073   if (IS_ACTIVE_BOMB(dynabomb_element))
6074   {
6075     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6076     dynabomb_size = player->dynabomb_size;
6077     dynabomb_xl = player->dynabomb_xl;
6078     player->dynabombs_left++;
6079   }
6080
6081   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6082
6083   for (i = 0; i < NUM_DIRECTIONS; i++)
6084   {
6085     for (j = 1; j <= dynabomb_size; j++)
6086     {
6087       int x = ex + j * xy[i][0];
6088       int y = ey + j * xy[i][1];
6089       int element;
6090
6091       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6092         break;
6093
6094       element = Tile[x][y];
6095
6096       // do not restart explosions of fields with active bombs
6097       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6098         continue;
6099
6100       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6101
6102       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6103           !IS_DIGGABLE(element) && !dynabomb_xl)
6104         break;
6105     }
6106   }
6107 }
6108
6109 void Bang(int x, int y)
6110 {
6111   int element = MovingOrBlocked2Element(x, y);
6112   int explosion_type = EX_TYPE_NORMAL;
6113
6114   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6115   {
6116     struct PlayerInfo *player = PLAYERINFO(x, y);
6117
6118     element = Tile[x][y] = player->initial_element;
6119
6120     if (level.use_explosion_element[player->index_nr])
6121     {
6122       int explosion_element = level.explosion_element[player->index_nr];
6123
6124       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6125         explosion_type = EX_TYPE_CROSS;
6126       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6127         explosion_type = EX_TYPE_CENTER;
6128     }
6129   }
6130
6131   switch (element)
6132   {
6133     case EL_BUG:
6134     case EL_SPACESHIP:
6135     case EL_BD_BUTTERFLY:
6136     case EL_BD_FIREFLY:
6137     case EL_YAMYAM:
6138     case EL_DARK_YAMYAM:
6139     case EL_ROBOT:
6140     case EL_PACMAN:
6141     case EL_MOLE:
6142       RaiseScoreElement(element);
6143       break;
6144
6145     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6146     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6147     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6148     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6149     case EL_DYNABOMB_INCREASE_NUMBER:
6150     case EL_DYNABOMB_INCREASE_SIZE:
6151     case EL_DYNABOMB_INCREASE_POWER:
6152       explosion_type = EX_TYPE_DYNA;
6153       break;
6154
6155     case EL_DC_LANDMINE:
6156       explosion_type = EX_TYPE_CENTER;
6157       break;
6158
6159     case EL_PENGUIN:
6160     case EL_LAMP:
6161     case EL_LAMP_ACTIVE:
6162     case EL_AMOEBA_TO_DIAMOND:
6163       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6164         explosion_type = EX_TYPE_CENTER;
6165       break;
6166
6167     default:
6168       if (element_info[element].explosion_type == EXPLODES_CROSS)
6169         explosion_type = EX_TYPE_CROSS;
6170       else if (element_info[element].explosion_type == EXPLODES_1X1)
6171         explosion_type = EX_TYPE_CENTER;
6172       break;
6173   }
6174
6175   if (explosion_type == EX_TYPE_DYNA)
6176     DynaExplode(x, y);
6177   else
6178     Explode(x, y, EX_PHASE_START, explosion_type);
6179
6180   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6181 }
6182
6183 static void SplashAcid(int x, int y)
6184 {
6185   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6186       (!IN_LEV_FIELD(x - 1, y - 2) ||
6187        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6188     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6189
6190   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6191       (!IN_LEV_FIELD(x + 1, y - 2) ||
6192        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6193     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6194
6195   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6196 }
6197
6198 static void InitBeltMovement(void)
6199 {
6200   static int belt_base_element[4] =
6201   {
6202     EL_CONVEYOR_BELT_1_LEFT,
6203     EL_CONVEYOR_BELT_2_LEFT,
6204     EL_CONVEYOR_BELT_3_LEFT,
6205     EL_CONVEYOR_BELT_4_LEFT
6206   };
6207   static int belt_base_active_element[4] =
6208   {
6209     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6210     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6211     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6212     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6213   };
6214
6215   int x, y, i, j;
6216
6217   // set frame order for belt animation graphic according to belt direction
6218   for (i = 0; i < NUM_BELTS; i++)
6219   {
6220     int belt_nr = i;
6221
6222     for (j = 0; j < NUM_BELT_PARTS; j++)
6223     {
6224       int element = belt_base_active_element[belt_nr] + j;
6225       int graphic_1 = el2img(element);
6226       int graphic_2 = el2panelimg(element);
6227
6228       if (game.belt_dir[i] == MV_LEFT)
6229       {
6230         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6231         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6232       }
6233       else
6234       {
6235         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6236         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6237       }
6238     }
6239   }
6240
6241   SCAN_PLAYFIELD(x, y)
6242   {
6243     int element = Tile[x][y];
6244
6245     for (i = 0; i < NUM_BELTS; i++)
6246     {
6247       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6248       {
6249         int e_belt_nr = getBeltNrFromBeltElement(element);
6250         int belt_nr = i;
6251
6252         if (e_belt_nr == belt_nr)
6253         {
6254           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6255
6256           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6257         }
6258       }
6259     }
6260   }
6261 }
6262
6263 static void ToggleBeltSwitch(int x, int y)
6264 {
6265   static int belt_base_element[4] =
6266   {
6267     EL_CONVEYOR_BELT_1_LEFT,
6268     EL_CONVEYOR_BELT_2_LEFT,
6269     EL_CONVEYOR_BELT_3_LEFT,
6270     EL_CONVEYOR_BELT_4_LEFT
6271   };
6272   static int belt_base_active_element[4] =
6273   {
6274     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6275     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6276     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6277     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6278   };
6279   static int belt_base_switch_element[4] =
6280   {
6281     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6282     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6283     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6284     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6285   };
6286   static int belt_move_dir[4] =
6287   {
6288     MV_LEFT,
6289     MV_NONE,
6290     MV_RIGHT,
6291     MV_NONE,
6292   };
6293
6294   int element = Tile[x][y];
6295   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6296   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6297   int belt_dir = belt_move_dir[belt_dir_nr];
6298   int xx, yy, i;
6299
6300   if (!IS_BELT_SWITCH(element))
6301     return;
6302
6303   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6304   game.belt_dir[belt_nr] = belt_dir;
6305
6306   if (belt_dir_nr == 3)
6307     belt_dir_nr = 1;
6308
6309   // set frame order for belt animation graphic according to belt direction
6310   for (i = 0; i < NUM_BELT_PARTS; i++)
6311   {
6312     int element = belt_base_active_element[belt_nr] + i;
6313     int graphic_1 = el2img(element);
6314     int graphic_2 = el2panelimg(element);
6315
6316     if (belt_dir == MV_LEFT)
6317     {
6318       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6319       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6320     }
6321     else
6322     {
6323       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6324       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6325     }
6326   }
6327
6328   SCAN_PLAYFIELD(xx, yy)
6329   {
6330     int element = Tile[xx][yy];
6331
6332     if (IS_BELT_SWITCH(element))
6333     {
6334       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6335
6336       if (e_belt_nr == belt_nr)
6337       {
6338         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6339         TEST_DrawLevelField(xx, yy);
6340       }
6341     }
6342     else if (IS_BELT(element) && belt_dir != MV_NONE)
6343     {
6344       int e_belt_nr = getBeltNrFromBeltElement(element);
6345
6346       if (e_belt_nr == belt_nr)
6347       {
6348         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6349
6350         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6351         TEST_DrawLevelField(xx, yy);
6352       }
6353     }
6354     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6355     {
6356       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6357
6358       if (e_belt_nr == belt_nr)
6359       {
6360         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6361
6362         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6363         TEST_DrawLevelField(xx, yy);
6364       }
6365     }
6366   }
6367 }
6368
6369 static void ToggleSwitchgateSwitch(int x, int y)
6370 {
6371   int xx, yy;
6372
6373   game.switchgate_pos = !game.switchgate_pos;
6374
6375   SCAN_PLAYFIELD(xx, yy)
6376   {
6377     int element = Tile[xx][yy];
6378
6379     if (element == EL_SWITCHGATE_SWITCH_UP)
6380     {
6381       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6382       TEST_DrawLevelField(xx, yy);
6383     }
6384     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6385     {
6386       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6387       TEST_DrawLevelField(xx, yy);
6388     }
6389     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6390     {
6391       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6392       TEST_DrawLevelField(xx, yy);
6393     }
6394     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6395     {
6396       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6397       TEST_DrawLevelField(xx, yy);
6398     }
6399     else if (element == EL_SWITCHGATE_OPEN ||
6400              element == EL_SWITCHGATE_OPENING)
6401     {
6402       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6403
6404       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6405     }
6406     else if (element == EL_SWITCHGATE_CLOSED ||
6407              element == EL_SWITCHGATE_CLOSING)
6408     {
6409       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6410
6411       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6412     }
6413   }
6414 }
6415
6416 static int getInvisibleActiveFromInvisibleElement(int element)
6417 {
6418   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6419           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6420           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6421           element);
6422 }
6423
6424 static int getInvisibleFromInvisibleActiveElement(int element)
6425 {
6426   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6427           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6428           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6429           element);
6430 }
6431
6432 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6433 {
6434   int x, y;
6435
6436   SCAN_PLAYFIELD(x, y)
6437   {
6438     int element = Tile[x][y];
6439
6440     if (element == EL_LIGHT_SWITCH &&
6441         game.light_time_left > 0)
6442     {
6443       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6444       TEST_DrawLevelField(x, y);
6445     }
6446     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6447              game.light_time_left == 0)
6448     {
6449       Tile[x][y] = EL_LIGHT_SWITCH;
6450       TEST_DrawLevelField(x, y);
6451     }
6452     else if (element == EL_EMC_DRIPPER &&
6453              game.light_time_left > 0)
6454     {
6455       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6456       TEST_DrawLevelField(x, y);
6457     }
6458     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6459              game.light_time_left == 0)
6460     {
6461       Tile[x][y] = EL_EMC_DRIPPER;
6462       TEST_DrawLevelField(x, y);
6463     }
6464     else if (element == EL_INVISIBLE_STEELWALL ||
6465              element == EL_INVISIBLE_WALL ||
6466              element == EL_INVISIBLE_SAND)
6467     {
6468       if (game.light_time_left > 0)
6469         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6470
6471       TEST_DrawLevelField(x, y);
6472
6473       // uncrumble neighbour fields, if needed
6474       if (element == EL_INVISIBLE_SAND)
6475         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6476     }
6477     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6478              element == EL_INVISIBLE_WALL_ACTIVE ||
6479              element == EL_INVISIBLE_SAND_ACTIVE)
6480     {
6481       if (game.light_time_left == 0)
6482         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6483
6484       TEST_DrawLevelField(x, y);
6485
6486       // re-crumble neighbour fields, if needed
6487       if (element == EL_INVISIBLE_SAND)
6488         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6489     }
6490   }
6491 }
6492
6493 static void RedrawAllInvisibleElementsForLenses(void)
6494 {
6495   int x, y;
6496
6497   SCAN_PLAYFIELD(x, y)
6498   {
6499     int element = Tile[x][y];
6500
6501     if (element == EL_EMC_DRIPPER &&
6502         game.lenses_time_left > 0)
6503     {
6504       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6505       TEST_DrawLevelField(x, y);
6506     }
6507     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6508              game.lenses_time_left == 0)
6509     {
6510       Tile[x][y] = EL_EMC_DRIPPER;
6511       TEST_DrawLevelField(x, y);
6512     }
6513     else if (element == EL_INVISIBLE_STEELWALL ||
6514              element == EL_INVISIBLE_WALL ||
6515              element == EL_INVISIBLE_SAND)
6516     {
6517       if (game.lenses_time_left > 0)
6518         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6519
6520       TEST_DrawLevelField(x, y);
6521
6522       // uncrumble neighbour fields, if needed
6523       if (element == EL_INVISIBLE_SAND)
6524         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6525     }
6526     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6527              element == EL_INVISIBLE_WALL_ACTIVE ||
6528              element == EL_INVISIBLE_SAND_ACTIVE)
6529     {
6530       if (game.lenses_time_left == 0)
6531         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6532
6533       TEST_DrawLevelField(x, y);
6534
6535       // re-crumble neighbour fields, if needed
6536       if (element == EL_INVISIBLE_SAND)
6537         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6538     }
6539   }
6540 }
6541
6542 static void RedrawAllInvisibleElementsForMagnifier(void)
6543 {
6544   int x, y;
6545
6546   SCAN_PLAYFIELD(x, y)
6547   {
6548     int element = Tile[x][y];
6549
6550     if (element == EL_EMC_FAKE_GRASS &&
6551         game.magnify_time_left > 0)
6552     {
6553       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6554       TEST_DrawLevelField(x, y);
6555     }
6556     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6557              game.magnify_time_left == 0)
6558     {
6559       Tile[x][y] = EL_EMC_FAKE_GRASS;
6560       TEST_DrawLevelField(x, y);
6561     }
6562     else if (IS_GATE_GRAY(element) &&
6563              game.magnify_time_left > 0)
6564     {
6565       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6566                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6567                     IS_EM_GATE_GRAY(element) ?
6568                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6569                     IS_EMC_GATE_GRAY(element) ?
6570                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6571                     IS_DC_GATE_GRAY(element) ?
6572                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6573                     element);
6574       TEST_DrawLevelField(x, y);
6575     }
6576     else if (IS_GATE_GRAY_ACTIVE(element) &&
6577              game.magnify_time_left == 0)
6578     {
6579       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6580                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6581                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6582                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6583                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6584                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6585                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6586                     EL_DC_GATE_WHITE_GRAY :
6587                     element);
6588       TEST_DrawLevelField(x, y);
6589     }
6590   }
6591 }
6592
6593 static void ToggleLightSwitch(int x, int y)
6594 {
6595   int element = Tile[x][y];
6596
6597   game.light_time_left =
6598     (element == EL_LIGHT_SWITCH ?
6599      level.time_light * FRAMES_PER_SECOND : 0);
6600
6601   RedrawAllLightSwitchesAndInvisibleElements();
6602 }
6603
6604 static void ActivateTimegateSwitch(int x, int y)
6605 {
6606   int xx, yy;
6607
6608   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6609
6610   SCAN_PLAYFIELD(xx, yy)
6611   {
6612     int element = Tile[xx][yy];
6613
6614     if (element == EL_TIMEGATE_CLOSED ||
6615         element == EL_TIMEGATE_CLOSING)
6616     {
6617       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6618       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6619     }
6620
6621     /*
6622     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6623     {
6624       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6625       TEST_DrawLevelField(xx, yy);
6626     }
6627     */
6628
6629   }
6630
6631   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6632                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6633 }
6634
6635 static void Impact(int x, int y)
6636 {
6637   boolean last_line = (y == lev_fieldy - 1);
6638   boolean object_hit = FALSE;
6639   boolean impact = (last_line || object_hit);
6640   int element = Tile[x][y];
6641   int smashed = EL_STEELWALL;
6642
6643   if (!last_line)       // check if element below was hit
6644   {
6645     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6646       return;
6647
6648     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6649                                          MovDir[x][y + 1] != MV_DOWN ||
6650                                          MovPos[x][y + 1] <= TILEY / 2));
6651
6652     // do not smash moving elements that left the smashed field in time
6653     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6654         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6655       object_hit = FALSE;
6656
6657 #if USE_QUICKSAND_IMPACT_BUGFIX
6658     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6659     {
6660       RemoveMovingField(x, y + 1);
6661       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6662       Tile[x][y + 2] = EL_ROCK;
6663       TEST_DrawLevelField(x, y + 2);
6664
6665       object_hit = TRUE;
6666     }
6667
6668     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6669     {
6670       RemoveMovingField(x, y + 1);
6671       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6672       Tile[x][y + 2] = EL_ROCK;
6673       TEST_DrawLevelField(x, y + 2);
6674
6675       object_hit = TRUE;
6676     }
6677 #endif
6678
6679     if (object_hit)
6680       smashed = MovingOrBlocked2Element(x, y + 1);
6681
6682     impact = (last_line || object_hit);
6683   }
6684
6685   if (!last_line && smashed == EL_ACID) // element falls into acid
6686   {
6687     SplashAcid(x, y + 1);
6688     return;
6689   }
6690
6691   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6692   // only reset graphic animation if graphic really changes after impact
6693   if (impact &&
6694       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6695   {
6696     ResetGfxAnimation(x, y);
6697     TEST_DrawLevelField(x, y);
6698   }
6699
6700   if (impact && CAN_EXPLODE_IMPACT(element))
6701   {
6702     Bang(x, y);
6703     return;
6704   }
6705   else if (impact && element == EL_PEARL &&
6706            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6707   {
6708     ResetGfxAnimation(x, y);
6709
6710     Tile[x][y] = EL_PEARL_BREAKING;
6711     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6712     return;
6713   }
6714   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6715   {
6716     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6717
6718     return;
6719   }
6720
6721   if (impact && element == EL_AMOEBA_DROP)
6722   {
6723     if (object_hit && IS_PLAYER(x, y + 1))
6724       KillPlayerUnlessEnemyProtected(x, y + 1);
6725     else if (object_hit && smashed == EL_PENGUIN)
6726       Bang(x, y + 1);
6727     else
6728     {
6729       Tile[x][y] = EL_AMOEBA_GROWING;
6730       Store[x][y] = EL_AMOEBA_WET;
6731
6732       ResetRandomAnimationValue(x, y);
6733     }
6734     return;
6735   }
6736
6737   if (object_hit)               // check which object was hit
6738   {
6739     if ((CAN_PASS_MAGIC_WALL(element) && 
6740          (smashed == EL_MAGIC_WALL ||
6741           smashed == EL_BD_MAGIC_WALL)) ||
6742         (CAN_PASS_DC_MAGIC_WALL(element) &&
6743          smashed == EL_DC_MAGIC_WALL))
6744     {
6745       int xx, yy;
6746       int activated_magic_wall =
6747         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6748          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6749          EL_DC_MAGIC_WALL_ACTIVE);
6750
6751       // activate magic wall / mill
6752       SCAN_PLAYFIELD(xx, yy)
6753       {
6754         if (Tile[xx][yy] == smashed)
6755           Tile[xx][yy] = activated_magic_wall;
6756       }
6757
6758       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6759       game.magic_wall_active = TRUE;
6760
6761       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6762                             SND_MAGIC_WALL_ACTIVATING :
6763                             smashed == EL_BD_MAGIC_WALL ?
6764                             SND_BD_MAGIC_WALL_ACTIVATING :
6765                             SND_DC_MAGIC_WALL_ACTIVATING));
6766     }
6767
6768     if (IS_PLAYER(x, y + 1))
6769     {
6770       if (CAN_SMASH_PLAYER(element))
6771       {
6772         KillPlayerUnlessEnemyProtected(x, y + 1);
6773         return;
6774       }
6775     }
6776     else if (smashed == EL_PENGUIN)
6777     {
6778       if (CAN_SMASH_PLAYER(element))
6779       {
6780         Bang(x, y + 1);
6781         return;
6782       }
6783     }
6784     else if (element == EL_BD_DIAMOND)
6785     {
6786       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6787       {
6788         Bang(x, y + 1);
6789         return;
6790       }
6791     }
6792     else if (((element == EL_SP_INFOTRON ||
6793                element == EL_SP_ZONK) &&
6794               (smashed == EL_SP_SNIKSNAK ||
6795                smashed == EL_SP_ELECTRON ||
6796                smashed == EL_SP_DISK_ORANGE)) ||
6797              (element == EL_SP_INFOTRON &&
6798               smashed == EL_SP_DISK_YELLOW))
6799     {
6800       Bang(x, y + 1);
6801       return;
6802     }
6803     else if (CAN_SMASH_EVERYTHING(element))
6804     {
6805       if (IS_CLASSIC_ENEMY(smashed) ||
6806           CAN_EXPLODE_SMASHED(smashed))
6807       {
6808         Bang(x, y + 1);
6809         return;
6810       }
6811       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6812       {
6813         if (smashed == EL_LAMP ||
6814             smashed == EL_LAMP_ACTIVE)
6815         {
6816           Bang(x, y + 1);
6817           return;
6818         }
6819         else if (smashed == EL_NUT)
6820         {
6821           Tile[x][y + 1] = EL_NUT_BREAKING;
6822           PlayLevelSound(x, y, SND_NUT_BREAKING);
6823           RaiseScoreElement(EL_NUT);
6824           return;
6825         }
6826         else if (smashed == EL_PEARL)
6827         {
6828           ResetGfxAnimation(x, y);
6829
6830           Tile[x][y + 1] = EL_PEARL_BREAKING;
6831           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6832           return;
6833         }
6834         else if (smashed == EL_DIAMOND)
6835         {
6836           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6837           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6838           return;
6839         }
6840         else if (IS_BELT_SWITCH(smashed))
6841         {
6842           ToggleBeltSwitch(x, y + 1);
6843         }
6844         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6845                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6846                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6847                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6848         {
6849           ToggleSwitchgateSwitch(x, y + 1);
6850         }
6851         else if (smashed == EL_LIGHT_SWITCH ||
6852                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6853         {
6854           ToggleLightSwitch(x, y + 1);
6855         }
6856         else
6857         {
6858           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6859
6860           CheckElementChangeBySide(x, y + 1, smashed, element,
6861                                    CE_SWITCHED, CH_SIDE_TOP);
6862           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6863                                             CH_SIDE_TOP);
6864         }
6865       }
6866       else
6867       {
6868         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6869       }
6870     }
6871   }
6872
6873   // play sound of magic wall / mill
6874   if (!last_line &&
6875       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6876        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6877        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6878   {
6879     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6880       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6881     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6882       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6883     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6884       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6885
6886     return;
6887   }
6888
6889   // play sound of object that hits the ground
6890   if (last_line || object_hit)
6891     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6892 }
6893
6894 static void TurnRoundExt(int x, int y)
6895 {
6896   static struct
6897   {
6898     int dx, dy;
6899   } move_xy[] =
6900   {
6901     {  0,  0 },
6902     { -1,  0 },
6903     { +1,  0 },
6904     {  0,  0 },
6905     {  0, -1 },
6906     {  0,  0 }, { 0, 0 }, { 0, 0 },
6907     {  0, +1 }
6908   };
6909   static struct
6910   {
6911     int left, right, back;
6912   } turn[] =
6913   {
6914     { 0,        0,              0        },
6915     { MV_DOWN,  MV_UP,          MV_RIGHT },
6916     { MV_UP,    MV_DOWN,        MV_LEFT  },
6917     { 0,        0,              0        },
6918     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6919     { 0,        0,              0        },
6920     { 0,        0,              0        },
6921     { 0,        0,              0        },
6922     { MV_RIGHT, MV_LEFT,        MV_UP    }
6923   };
6924
6925   int element = Tile[x][y];
6926   int move_pattern = element_info[element].move_pattern;
6927
6928   int old_move_dir = MovDir[x][y];
6929   int left_dir  = turn[old_move_dir].left;
6930   int right_dir = turn[old_move_dir].right;
6931   int back_dir  = turn[old_move_dir].back;
6932
6933   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6934   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6935   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6936   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6937
6938   int left_x  = x + left_dx,  left_y  = y + left_dy;
6939   int right_x = x + right_dx, right_y = y + right_dy;
6940   int move_x  = x + move_dx,  move_y  = y + move_dy;
6941
6942   int xx, yy;
6943
6944   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6945   {
6946     TestIfBadThingTouchesOtherBadThing(x, y);
6947
6948     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6949       MovDir[x][y] = right_dir;
6950     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6951       MovDir[x][y] = left_dir;
6952
6953     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6954       MovDelay[x][y] = 9;
6955     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6956       MovDelay[x][y] = 1;
6957   }
6958   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6959   {
6960     TestIfBadThingTouchesOtherBadThing(x, y);
6961
6962     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6963       MovDir[x][y] = left_dir;
6964     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6965       MovDir[x][y] = right_dir;
6966
6967     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6968       MovDelay[x][y] = 9;
6969     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6970       MovDelay[x][y] = 1;
6971   }
6972   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6973   {
6974     TestIfBadThingTouchesOtherBadThing(x, y);
6975
6976     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6977       MovDir[x][y] = left_dir;
6978     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6979       MovDir[x][y] = right_dir;
6980
6981     if (MovDir[x][y] != old_move_dir)
6982       MovDelay[x][y] = 9;
6983   }
6984   else if (element == EL_YAMYAM)
6985   {
6986     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6987     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6988
6989     if (can_turn_left && can_turn_right)
6990       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6991     else if (can_turn_left)
6992       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6993     else if (can_turn_right)
6994       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6995     else
6996       MovDir[x][y] = back_dir;
6997
6998     MovDelay[x][y] = 16 + 16 * RND(3);
6999   }
7000   else if (element == EL_DARK_YAMYAM)
7001   {
7002     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7003                                                          left_x, left_y);
7004     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7005                                                          right_x, right_y);
7006
7007     if (can_turn_left && can_turn_right)
7008       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7009     else if (can_turn_left)
7010       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7011     else if (can_turn_right)
7012       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7013     else
7014       MovDir[x][y] = back_dir;
7015
7016     MovDelay[x][y] = 16 + 16 * RND(3);
7017   }
7018   else if (element == EL_PACMAN)
7019   {
7020     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7021     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7022
7023     if (can_turn_left && can_turn_right)
7024       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7025     else if (can_turn_left)
7026       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7027     else if (can_turn_right)
7028       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7029     else
7030       MovDir[x][y] = back_dir;
7031
7032     MovDelay[x][y] = 6 + RND(40);
7033   }
7034   else if (element == EL_PIG)
7035   {
7036     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7037     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7038     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7039     boolean should_turn_left, should_turn_right, should_move_on;
7040     int rnd_value = 24;
7041     int rnd = RND(rnd_value);
7042
7043     should_turn_left = (can_turn_left &&
7044                         (!can_move_on ||
7045                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7046                                                    y + back_dy + left_dy)));
7047     should_turn_right = (can_turn_right &&
7048                          (!can_move_on ||
7049                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7050                                                     y + back_dy + right_dy)));
7051     should_move_on = (can_move_on &&
7052                       (!can_turn_left ||
7053                        !can_turn_right ||
7054                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7055                                                  y + move_dy + left_dy) ||
7056                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7057                                                  y + move_dy + right_dy)));
7058
7059     if (should_turn_left || should_turn_right || should_move_on)
7060     {
7061       if (should_turn_left && should_turn_right && should_move_on)
7062         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7063                         rnd < 2 * rnd_value / 3 ? right_dir :
7064                         old_move_dir);
7065       else if (should_turn_left && should_turn_right)
7066         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7067       else if (should_turn_left && should_move_on)
7068         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7069       else if (should_turn_right && should_move_on)
7070         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7071       else if (should_turn_left)
7072         MovDir[x][y] = left_dir;
7073       else if (should_turn_right)
7074         MovDir[x][y] = right_dir;
7075       else if (should_move_on)
7076         MovDir[x][y] = old_move_dir;
7077     }
7078     else if (can_move_on && rnd > rnd_value / 8)
7079       MovDir[x][y] = old_move_dir;
7080     else if (can_turn_left && can_turn_right)
7081       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7082     else if (can_turn_left && rnd > rnd_value / 8)
7083       MovDir[x][y] = left_dir;
7084     else if (can_turn_right && rnd > rnd_value/8)
7085       MovDir[x][y] = right_dir;
7086     else
7087       MovDir[x][y] = back_dir;
7088
7089     xx = x + move_xy[MovDir[x][y]].dx;
7090     yy = y + move_xy[MovDir[x][y]].dy;
7091
7092     if (!IN_LEV_FIELD(xx, yy) ||
7093         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7094       MovDir[x][y] = old_move_dir;
7095
7096     MovDelay[x][y] = 0;
7097   }
7098   else if (element == EL_DRAGON)
7099   {
7100     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7101     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7102     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7103     int rnd_value = 24;
7104     int rnd = RND(rnd_value);
7105
7106     if (can_move_on && rnd > rnd_value / 8)
7107       MovDir[x][y] = old_move_dir;
7108     else if (can_turn_left && can_turn_right)
7109       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7110     else if (can_turn_left && rnd > rnd_value / 8)
7111       MovDir[x][y] = left_dir;
7112     else if (can_turn_right && rnd > rnd_value / 8)
7113       MovDir[x][y] = right_dir;
7114     else
7115       MovDir[x][y] = back_dir;
7116
7117     xx = x + move_xy[MovDir[x][y]].dx;
7118     yy = y + move_xy[MovDir[x][y]].dy;
7119
7120     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7121       MovDir[x][y] = old_move_dir;
7122
7123     MovDelay[x][y] = 0;
7124   }
7125   else if (element == EL_MOLE)
7126   {
7127     boolean can_move_on =
7128       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7129                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7130                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7131     if (!can_move_on)
7132     {
7133       boolean can_turn_left =
7134         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7135                               IS_AMOEBOID(Tile[left_x][left_y])));
7136
7137       boolean can_turn_right =
7138         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7139                               IS_AMOEBOID(Tile[right_x][right_y])));
7140
7141       if (can_turn_left && can_turn_right)
7142         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7143       else if (can_turn_left)
7144         MovDir[x][y] = left_dir;
7145       else
7146         MovDir[x][y] = right_dir;
7147     }
7148
7149     if (MovDir[x][y] != old_move_dir)
7150       MovDelay[x][y] = 9;
7151   }
7152   else if (element == EL_BALLOON)
7153   {
7154     MovDir[x][y] = game.wind_direction;
7155     MovDelay[x][y] = 0;
7156   }
7157   else if (element == EL_SPRING)
7158   {
7159     if (MovDir[x][y] & MV_HORIZONTAL)
7160     {
7161       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7162           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7163       {
7164         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7165         ResetGfxAnimation(move_x, move_y);
7166         TEST_DrawLevelField(move_x, move_y);
7167
7168         MovDir[x][y] = back_dir;
7169       }
7170       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7171                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7172         MovDir[x][y] = MV_NONE;
7173     }
7174
7175     MovDelay[x][y] = 0;
7176   }
7177   else if (element == EL_ROBOT ||
7178            element == EL_SATELLITE ||
7179            element == EL_PENGUIN ||
7180            element == EL_EMC_ANDROID)
7181   {
7182     int attr_x = -1, attr_y = -1;
7183
7184     if (game.all_players_gone)
7185     {
7186       attr_x = game.exit_x;
7187       attr_y = game.exit_y;
7188     }
7189     else
7190     {
7191       int i;
7192
7193       for (i = 0; i < MAX_PLAYERS; i++)
7194       {
7195         struct PlayerInfo *player = &stored_player[i];
7196         int jx = player->jx, jy = player->jy;
7197
7198         if (!player->active)
7199           continue;
7200
7201         if (attr_x == -1 ||
7202             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7203         {
7204           attr_x = jx;
7205           attr_y = jy;
7206         }
7207       }
7208     }
7209
7210     if (element == EL_ROBOT &&
7211         game.robot_wheel_x >= 0 &&
7212         game.robot_wheel_y >= 0 &&
7213         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7214          game.engine_version < VERSION_IDENT(3,1,0,0)))
7215     {
7216       attr_x = game.robot_wheel_x;
7217       attr_y = game.robot_wheel_y;
7218     }
7219
7220     if (element == EL_PENGUIN)
7221     {
7222       int i;
7223       static int xy[4][2] =
7224       {
7225         { 0, -1 },
7226         { -1, 0 },
7227         { +1, 0 },
7228         { 0, +1 }
7229       };
7230
7231       for (i = 0; i < NUM_DIRECTIONS; i++)
7232       {
7233         int ex = x + xy[i][0];
7234         int ey = y + xy[i][1];
7235
7236         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7237                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7238                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7239                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7240         {
7241           attr_x = ex;
7242           attr_y = ey;
7243           break;
7244         }
7245       }
7246     }
7247
7248     MovDir[x][y] = MV_NONE;
7249     if (attr_x < x)
7250       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7251     else if (attr_x > x)
7252       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7253     if (attr_y < y)
7254       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7255     else if (attr_y > y)
7256       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7257
7258     if (element == EL_ROBOT)
7259     {
7260       int newx, newy;
7261
7262       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7263         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7264       Moving2Blocked(x, y, &newx, &newy);
7265
7266       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7267         MovDelay[x][y] = 8 + 8 * !RND(3);
7268       else
7269         MovDelay[x][y] = 16;
7270     }
7271     else if (element == EL_PENGUIN)
7272     {
7273       int newx, newy;
7274
7275       MovDelay[x][y] = 1;
7276
7277       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7278       {
7279         boolean first_horiz = RND(2);
7280         int new_move_dir = MovDir[x][y];
7281
7282         MovDir[x][y] =
7283           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7284         Moving2Blocked(x, y, &newx, &newy);
7285
7286         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7287           return;
7288
7289         MovDir[x][y] =
7290           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7291         Moving2Blocked(x, y, &newx, &newy);
7292
7293         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7294           return;
7295
7296         MovDir[x][y] = old_move_dir;
7297         return;
7298       }
7299     }
7300     else if (element == EL_SATELLITE)
7301     {
7302       int newx, newy;
7303
7304       MovDelay[x][y] = 1;
7305
7306       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7307       {
7308         boolean first_horiz = RND(2);
7309         int new_move_dir = MovDir[x][y];
7310
7311         MovDir[x][y] =
7312           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7313         Moving2Blocked(x, y, &newx, &newy);
7314
7315         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7316           return;
7317
7318         MovDir[x][y] =
7319           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7320         Moving2Blocked(x, y, &newx, &newy);
7321
7322         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7323           return;
7324
7325         MovDir[x][y] = old_move_dir;
7326         return;
7327       }
7328     }
7329     else if (element == EL_EMC_ANDROID)
7330     {
7331       static int check_pos[16] =
7332       {
7333         -1,             //  0 => (invalid)
7334         7,              //  1 => MV_LEFT
7335         3,              //  2 => MV_RIGHT
7336         -1,             //  3 => (invalid)
7337         1,              //  4 =>            MV_UP
7338         0,              //  5 => MV_LEFT  | MV_UP
7339         2,              //  6 => MV_RIGHT | MV_UP
7340         -1,             //  7 => (invalid)
7341         5,              //  8 =>            MV_DOWN
7342         6,              //  9 => MV_LEFT  | MV_DOWN
7343         4,              // 10 => MV_RIGHT | MV_DOWN
7344         -1,             // 11 => (invalid)
7345         -1,             // 12 => (invalid)
7346         -1,             // 13 => (invalid)
7347         -1,             // 14 => (invalid)
7348         -1,             // 15 => (invalid)
7349       };
7350       static struct
7351       {
7352         int dx, dy;
7353         int dir;
7354       } check_xy[8] =
7355       {
7356         { -1, -1,       MV_LEFT  | MV_UP   },
7357         {  0, -1,                  MV_UP   },
7358         { +1, -1,       MV_RIGHT | MV_UP   },
7359         { +1,  0,       MV_RIGHT           },
7360         { +1, +1,       MV_RIGHT | MV_DOWN },
7361         {  0, +1,                  MV_DOWN },
7362         { -1, +1,       MV_LEFT  | MV_DOWN },
7363         { -1,  0,       MV_LEFT            },
7364       };
7365       int start_pos, check_order;
7366       boolean can_clone = FALSE;
7367       int i;
7368
7369       // check if there is any free field around current position
7370       for (i = 0; i < 8; i++)
7371       {
7372         int newx = x + check_xy[i].dx;
7373         int newy = y + check_xy[i].dy;
7374
7375         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7376         {
7377           can_clone = TRUE;
7378
7379           break;
7380         }
7381       }
7382
7383       if (can_clone)            // randomly find an element to clone
7384       {
7385         can_clone = FALSE;
7386
7387         start_pos = check_pos[RND(8)];
7388         check_order = (RND(2) ? -1 : +1);
7389
7390         for (i = 0; i < 8; i++)
7391         {
7392           int pos_raw = start_pos + i * check_order;
7393           int pos = (pos_raw + 8) % 8;
7394           int newx = x + check_xy[pos].dx;
7395           int newy = y + check_xy[pos].dy;
7396
7397           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7398           {
7399             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7400             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7401
7402             Store[x][y] = Tile[newx][newy];
7403
7404             can_clone = TRUE;
7405
7406             break;
7407           }
7408         }
7409       }
7410
7411       if (can_clone)            // randomly find a direction to move
7412       {
7413         can_clone = FALSE;
7414
7415         start_pos = check_pos[RND(8)];
7416         check_order = (RND(2) ? -1 : +1);
7417
7418         for (i = 0; i < 8; i++)
7419         {
7420           int pos_raw = start_pos + i * check_order;
7421           int pos = (pos_raw + 8) % 8;
7422           int newx = x + check_xy[pos].dx;
7423           int newy = y + check_xy[pos].dy;
7424           int new_move_dir = check_xy[pos].dir;
7425
7426           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7427           {
7428             MovDir[x][y] = new_move_dir;
7429             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7430
7431             can_clone = TRUE;
7432
7433             break;
7434           }
7435         }
7436       }
7437
7438       if (can_clone)            // cloning and moving successful
7439         return;
7440
7441       // cannot clone -- try to move towards player
7442
7443       start_pos = check_pos[MovDir[x][y] & 0x0f];
7444       check_order = (RND(2) ? -1 : +1);
7445
7446       for (i = 0; i < 3; i++)
7447       {
7448         // first check start_pos, then previous/next or (next/previous) pos
7449         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7450         int pos = (pos_raw + 8) % 8;
7451         int newx = x + check_xy[pos].dx;
7452         int newy = y + check_xy[pos].dy;
7453         int new_move_dir = check_xy[pos].dir;
7454
7455         if (IS_PLAYER(newx, newy))
7456           break;
7457
7458         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7459         {
7460           MovDir[x][y] = new_move_dir;
7461           MovDelay[x][y] = level.android_move_time * 8 + 1;
7462
7463           break;
7464         }
7465       }
7466     }
7467   }
7468   else if (move_pattern == MV_TURNING_LEFT ||
7469            move_pattern == MV_TURNING_RIGHT ||
7470            move_pattern == MV_TURNING_LEFT_RIGHT ||
7471            move_pattern == MV_TURNING_RIGHT_LEFT ||
7472            move_pattern == MV_TURNING_RANDOM ||
7473            move_pattern == MV_ALL_DIRECTIONS)
7474   {
7475     boolean can_turn_left =
7476       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7477     boolean can_turn_right =
7478       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7479
7480     if (element_info[element].move_stepsize == 0)       // "not moving"
7481       return;
7482
7483     if (move_pattern == MV_TURNING_LEFT)
7484       MovDir[x][y] = left_dir;
7485     else if (move_pattern == MV_TURNING_RIGHT)
7486       MovDir[x][y] = right_dir;
7487     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7488       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7489     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7490       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7491     else if (move_pattern == MV_TURNING_RANDOM)
7492       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7493                       can_turn_right && !can_turn_left ? right_dir :
7494                       RND(2) ? left_dir : right_dir);
7495     else if (can_turn_left && can_turn_right)
7496       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7497     else if (can_turn_left)
7498       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7499     else if (can_turn_right)
7500       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7501     else
7502       MovDir[x][y] = back_dir;
7503
7504     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7505   }
7506   else if (move_pattern == MV_HORIZONTAL ||
7507            move_pattern == MV_VERTICAL)
7508   {
7509     if (move_pattern & old_move_dir)
7510       MovDir[x][y] = back_dir;
7511     else if (move_pattern == MV_HORIZONTAL)
7512       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7513     else if (move_pattern == MV_VERTICAL)
7514       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7515
7516     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7517   }
7518   else if (move_pattern & MV_ANY_DIRECTION)
7519   {
7520     MovDir[x][y] = move_pattern;
7521     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7522   }
7523   else if (move_pattern & MV_WIND_DIRECTION)
7524   {
7525     MovDir[x][y] = game.wind_direction;
7526     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7527   }
7528   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7529   {
7530     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7531       MovDir[x][y] = left_dir;
7532     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7533       MovDir[x][y] = right_dir;
7534
7535     if (MovDir[x][y] != old_move_dir)
7536       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7537   }
7538   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7539   {
7540     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7541       MovDir[x][y] = right_dir;
7542     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7543       MovDir[x][y] = left_dir;
7544
7545     if (MovDir[x][y] != old_move_dir)
7546       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7547   }
7548   else if (move_pattern == MV_TOWARDS_PLAYER ||
7549            move_pattern == MV_AWAY_FROM_PLAYER)
7550   {
7551     int attr_x = -1, attr_y = -1;
7552     int newx, newy;
7553     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7554
7555     if (game.all_players_gone)
7556     {
7557       attr_x = game.exit_x;
7558       attr_y = game.exit_y;
7559     }
7560     else
7561     {
7562       int i;
7563
7564       for (i = 0; i < MAX_PLAYERS; i++)
7565       {
7566         struct PlayerInfo *player = &stored_player[i];
7567         int jx = player->jx, jy = player->jy;
7568
7569         if (!player->active)
7570           continue;
7571
7572         if (attr_x == -1 ||
7573             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7574         {
7575           attr_x = jx;
7576           attr_y = jy;
7577         }
7578       }
7579     }
7580
7581     MovDir[x][y] = MV_NONE;
7582     if (attr_x < x)
7583       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7584     else if (attr_x > x)
7585       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7586     if (attr_y < y)
7587       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7588     else if (attr_y > y)
7589       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7590
7591     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7592
7593     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7594     {
7595       boolean first_horiz = RND(2);
7596       int new_move_dir = MovDir[x][y];
7597
7598       if (element_info[element].move_stepsize == 0)     // "not moving"
7599       {
7600         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7601         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7602
7603         return;
7604       }
7605
7606       MovDir[x][y] =
7607         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7608       Moving2Blocked(x, y, &newx, &newy);
7609
7610       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7611         return;
7612
7613       MovDir[x][y] =
7614         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7615       Moving2Blocked(x, y, &newx, &newy);
7616
7617       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7618         return;
7619
7620       MovDir[x][y] = old_move_dir;
7621     }
7622   }
7623   else if (move_pattern == MV_WHEN_PUSHED ||
7624            move_pattern == MV_WHEN_DROPPED)
7625   {
7626     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7627       MovDir[x][y] = MV_NONE;
7628
7629     MovDelay[x][y] = 0;
7630   }
7631   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7632   {
7633     static int test_xy[7][2] =
7634     {
7635       { 0, -1 },
7636       { -1, 0 },
7637       { +1, 0 },
7638       { 0, +1 },
7639       { 0, -1 },
7640       { -1, 0 },
7641       { +1, 0 },
7642     };
7643     static int test_dir[7] =
7644     {
7645       MV_UP,
7646       MV_LEFT,
7647       MV_RIGHT,
7648       MV_DOWN,
7649       MV_UP,
7650       MV_LEFT,
7651       MV_RIGHT,
7652     };
7653     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7654     int move_preference = -1000000;     // start with very low preference
7655     int new_move_dir = MV_NONE;
7656     int start_test = RND(4);
7657     int i;
7658
7659     for (i = 0; i < NUM_DIRECTIONS; i++)
7660     {
7661       int move_dir = test_dir[start_test + i];
7662       int move_dir_preference;
7663
7664       xx = x + test_xy[start_test + i][0];
7665       yy = y + test_xy[start_test + i][1];
7666
7667       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7668           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7669       {
7670         new_move_dir = move_dir;
7671
7672         break;
7673       }
7674
7675       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7676         continue;
7677
7678       move_dir_preference = -1 * RunnerVisit[xx][yy];
7679       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7680         move_dir_preference = PlayerVisit[xx][yy];
7681
7682       if (move_dir_preference > move_preference)
7683       {
7684         // prefer field that has not been visited for the longest time
7685         move_preference = move_dir_preference;
7686         new_move_dir = move_dir;
7687       }
7688       else if (move_dir_preference == move_preference &&
7689                move_dir == old_move_dir)
7690       {
7691         // prefer last direction when all directions are preferred equally
7692         move_preference = move_dir_preference;
7693         new_move_dir = move_dir;
7694       }
7695     }
7696
7697     MovDir[x][y] = new_move_dir;
7698     if (old_move_dir != new_move_dir)
7699       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7700   }
7701 }
7702
7703 static void TurnRound(int x, int y)
7704 {
7705   int direction = MovDir[x][y];
7706
7707   TurnRoundExt(x, y);
7708
7709   GfxDir[x][y] = MovDir[x][y];
7710
7711   if (direction != MovDir[x][y])
7712     GfxFrame[x][y] = 0;
7713
7714   if (MovDelay[x][y])
7715     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7716
7717   ResetGfxFrame(x, y);
7718 }
7719
7720 static boolean JustBeingPushed(int x, int y)
7721 {
7722   int i;
7723
7724   for (i = 0; i < MAX_PLAYERS; i++)
7725   {
7726     struct PlayerInfo *player = &stored_player[i];
7727
7728     if (player->active && player->is_pushing && player->MovPos)
7729     {
7730       int next_jx = player->jx + (player->jx - player->last_jx);
7731       int next_jy = player->jy + (player->jy - player->last_jy);
7732
7733       if (x == next_jx && y == next_jy)
7734         return TRUE;
7735     }
7736   }
7737
7738   return FALSE;
7739 }
7740
7741 static void StartMoving(int x, int y)
7742 {
7743   boolean started_moving = FALSE;       // some elements can fall _and_ move
7744   int element = Tile[x][y];
7745
7746   if (Stop[x][y])
7747     return;
7748
7749   if (MovDelay[x][y] == 0)
7750     GfxAction[x][y] = ACTION_DEFAULT;
7751
7752   if (CAN_FALL(element) && y < lev_fieldy - 1)
7753   {
7754     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7755         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7756       if (JustBeingPushed(x, y))
7757         return;
7758
7759     if (element == EL_QUICKSAND_FULL)
7760     {
7761       if (IS_FREE(x, y + 1))
7762       {
7763         InitMovingField(x, y, MV_DOWN);
7764         started_moving = TRUE;
7765
7766         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7767 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7768         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7769           Store[x][y] = EL_ROCK;
7770 #else
7771         Store[x][y] = EL_ROCK;
7772 #endif
7773
7774         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7775       }
7776       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7777       {
7778         if (!MovDelay[x][y])
7779         {
7780           MovDelay[x][y] = TILEY + 1;
7781
7782           ResetGfxAnimation(x, y);
7783           ResetGfxAnimation(x, y + 1);
7784         }
7785
7786         if (MovDelay[x][y])
7787         {
7788           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7789           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7790
7791           MovDelay[x][y]--;
7792           if (MovDelay[x][y])
7793             return;
7794         }
7795
7796         Tile[x][y] = EL_QUICKSAND_EMPTY;
7797         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7798         Store[x][y + 1] = Store[x][y];
7799         Store[x][y] = 0;
7800
7801         PlayLevelSoundAction(x, y, ACTION_FILLING);
7802       }
7803       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7804       {
7805         if (!MovDelay[x][y])
7806         {
7807           MovDelay[x][y] = TILEY + 1;
7808
7809           ResetGfxAnimation(x, y);
7810           ResetGfxAnimation(x, y + 1);
7811         }
7812
7813         if (MovDelay[x][y])
7814         {
7815           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7816           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7817
7818           MovDelay[x][y]--;
7819           if (MovDelay[x][y])
7820             return;
7821         }
7822
7823         Tile[x][y] = EL_QUICKSAND_EMPTY;
7824         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7825         Store[x][y + 1] = Store[x][y];
7826         Store[x][y] = 0;
7827
7828         PlayLevelSoundAction(x, y, ACTION_FILLING);
7829       }
7830     }
7831     else if (element == EL_QUICKSAND_FAST_FULL)
7832     {
7833       if (IS_FREE(x, y + 1))
7834       {
7835         InitMovingField(x, y, MV_DOWN);
7836         started_moving = TRUE;
7837
7838         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7839 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7840         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7841           Store[x][y] = EL_ROCK;
7842 #else
7843         Store[x][y] = EL_ROCK;
7844 #endif
7845
7846         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7847       }
7848       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7849       {
7850         if (!MovDelay[x][y])
7851         {
7852           MovDelay[x][y] = TILEY + 1;
7853
7854           ResetGfxAnimation(x, y);
7855           ResetGfxAnimation(x, y + 1);
7856         }
7857
7858         if (MovDelay[x][y])
7859         {
7860           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7861           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7862
7863           MovDelay[x][y]--;
7864           if (MovDelay[x][y])
7865             return;
7866         }
7867
7868         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7869         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7870         Store[x][y + 1] = Store[x][y];
7871         Store[x][y] = 0;
7872
7873         PlayLevelSoundAction(x, y, ACTION_FILLING);
7874       }
7875       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7876       {
7877         if (!MovDelay[x][y])
7878         {
7879           MovDelay[x][y] = TILEY + 1;
7880
7881           ResetGfxAnimation(x, y);
7882           ResetGfxAnimation(x, y + 1);
7883         }
7884
7885         if (MovDelay[x][y])
7886         {
7887           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7888           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7889
7890           MovDelay[x][y]--;
7891           if (MovDelay[x][y])
7892             return;
7893         }
7894
7895         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7896         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7897         Store[x][y + 1] = Store[x][y];
7898         Store[x][y] = 0;
7899
7900         PlayLevelSoundAction(x, y, ACTION_FILLING);
7901       }
7902     }
7903     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7904              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7905     {
7906       InitMovingField(x, y, MV_DOWN);
7907       started_moving = TRUE;
7908
7909       Tile[x][y] = EL_QUICKSAND_FILLING;
7910       Store[x][y] = element;
7911
7912       PlayLevelSoundAction(x, y, ACTION_FILLING);
7913     }
7914     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7915              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7916     {
7917       InitMovingField(x, y, MV_DOWN);
7918       started_moving = TRUE;
7919
7920       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7921       Store[x][y] = element;
7922
7923       PlayLevelSoundAction(x, y, ACTION_FILLING);
7924     }
7925     else if (element == EL_MAGIC_WALL_FULL)
7926     {
7927       if (IS_FREE(x, y + 1))
7928       {
7929         InitMovingField(x, y, MV_DOWN);
7930         started_moving = TRUE;
7931
7932         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7933         Store[x][y] = EL_CHANGED(Store[x][y]);
7934       }
7935       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7936       {
7937         if (!MovDelay[x][y])
7938           MovDelay[x][y] = TILEY / 4 + 1;
7939
7940         if (MovDelay[x][y])
7941         {
7942           MovDelay[x][y]--;
7943           if (MovDelay[x][y])
7944             return;
7945         }
7946
7947         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7948         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7949         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7950         Store[x][y] = 0;
7951       }
7952     }
7953     else if (element == EL_BD_MAGIC_WALL_FULL)
7954     {
7955       if (IS_FREE(x, y + 1))
7956       {
7957         InitMovingField(x, y, MV_DOWN);
7958         started_moving = TRUE;
7959
7960         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7961         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7962       }
7963       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7964       {
7965         if (!MovDelay[x][y])
7966           MovDelay[x][y] = TILEY / 4 + 1;
7967
7968         if (MovDelay[x][y])
7969         {
7970           MovDelay[x][y]--;
7971           if (MovDelay[x][y])
7972             return;
7973         }
7974
7975         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7976         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7977         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7978         Store[x][y] = 0;
7979       }
7980     }
7981     else if (element == EL_DC_MAGIC_WALL_FULL)
7982     {
7983       if (IS_FREE(x, y + 1))
7984       {
7985         InitMovingField(x, y, MV_DOWN);
7986         started_moving = TRUE;
7987
7988         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7989         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7990       }
7991       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7992       {
7993         if (!MovDelay[x][y])
7994           MovDelay[x][y] = TILEY / 4 + 1;
7995
7996         if (MovDelay[x][y])
7997         {
7998           MovDelay[x][y]--;
7999           if (MovDelay[x][y])
8000             return;
8001         }
8002
8003         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8004         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8005         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8006         Store[x][y] = 0;
8007       }
8008     }
8009     else if ((CAN_PASS_MAGIC_WALL(element) &&
8010               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8011                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8012              (CAN_PASS_DC_MAGIC_WALL(element) &&
8013               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8014
8015     {
8016       InitMovingField(x, y, MV_DOWN);
8017       started_moving = TRUE;
8018
8019       Tile[x][y] =
8020         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8021          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8022          EL_DC_MAGIC_WALL_FILLING);
8023       Store[x][y] = element;
8024     }
8025     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8026     {
8027       SplashAcid(x, y + 1);
8028
8029       InitMovingField(x, y, MV_DOWN);
8030       started_moving = TRUE;
8031
8032       Store[x][y] = EL_ACID;
8033     }
8034     else if (
8035              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8036               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8037              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8038               CAN_FALL(element) && WasJustFalling[x][y] &&
8039               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8040
8041              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8042               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8043               (Tile[x][y + 1] == EL_BLOCKED)))
8044     {
8045       /* this is needed for a special case not covered by calling "Impact()"
8046          from "ContinueMoving()": if an element moves to a tile directly below
8047          another element which was just falling on that tile (which was empty
8048          in the previous frame), the falling element above would just stop
8049          instead of smashing the element below (in previous version, the above
8050          element was just checked for "moving" instead of "falling", resulting
8051          in incorrect smashes caused by horizontal movement of the above
8052          element; also, the case of the player being the element to smash was
8053          simply not covered here... :-/ ) */
8054
8055       CheckCollision[x][y] = 0;
8056       CheckImpact[x][y] = 0;
8057
8058       Impact(x, y);
8059     }
8060     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8061     {
8062       if (MovDir[x][y] == MV_NONE)
8063       {
8064         InitMovingField(x, y, MV_DOWN);
8065         started_moving = TRUE;
8066       }
8067     }
8068     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8069     {
8070       if (WasJustFalling[x][y]) // prevent animation from being restarted
8071         MovDir[x][y] = MV_DOWN;
8072
8073       InitMovingField(x, y, MV_DOWN);
8074       started_moving = TRUE;
8075     }
8076     else if (element == EL_AMOEBA_DROP)
8077     {
8078       Tile[x][y] = EL_AMOEBA_GROWING;
8079       Store[x][y] = EL_AMOEBA_WET;
8080     }
8081     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8082               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8083              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8084              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8085     {
8086       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8087                                 (IS_FREE(x - 1, y + 1) ||
8088                                  Tile[x - 1][y + 1] == EL_ACID));
8089       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8090                                 (IS_FREE(x + 1, y + 1) ||
8091                                  Tile[x + 1][y + 1] == EL_ACID));
8092       boolean can_fall_any  = (can_fall_left || can_fall_right);
8093       boolean can_fall_both = (can_fall_left && can_fall_right);
8094       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8095
8096       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8097       {
8098         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8099           can_fall_right = FALSE;
8100         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8101           can_fall_left = FALSE;
8102         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8103           can_fall_right = FALSE;
8104         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8105           can_fall_left = FALSE;
8106
8107         can_fall_any  = (can_fall_left || can_fall_right);
8108         can_fall_both = FALSE;
8109       }
8110
8111       if (can_fall_both)
8112       {
8113         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8114           can_fall_right = FALSE;       // slip down on left side
8115         else
8116           can_fall_left = !(can_fall_right = RND(2));
8117
8118         can_fall_both = FALSE;
8119       }
8120
8121       if (can_fall_any)
8122       {
8123         // if not determined otherwise, prefer left side for slipping down
8124         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8125         started_moving = TRUE;
8126       }
8127     }
8128     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8129     {
8130       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8131       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8132       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8133       int belt_dir = game.belt_dir[belt_nr];
8134
8135       if ((belt_dir == MV_LEFT  && left_is_free) ||
8136           (belt_dir == MV_RIGHT && right_is_free))
8137       {
8138         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8139
8140         InitMovingField(x, y, belt_dir);
8141         started_moving = TRUE;
8142
8143         Pushed[x][y] = TRUE;
8144         Pushed[nextx][y] = TRUE;
8145
8146         GfxAction[x][y] = ACTION_DEFAULT;
8147       }
8148       else
8149       {
8150         MovDir[x][y] = 0;       // if element was moving, stop it
8151       }
8152     }
8153   }
8154
8155   // not "else if" because of elements that can fall and move (EL_SPRING)
8156   if (CAN_MOVE(element) && !started_moving)
8157   {
8158     int move_pattern = element_info[element].move_pattern;
8159     int newx, newy;
8160
8161     Moving2Blocked(x, y, &newx, &newy);
8162
8163     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8164       return;
8165
8166     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8167         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8168     {
8169       WasJustMoving[x][y] = 0;
8170       CheckCollision[x][y] = 0;
8171
8172       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8173
8174       if (Tile[x][y] != element)        // element has changed
8175         return;
8176     }
8177
8178     if (!MovDelay[x][y])        // start new movement phase
8179     {
8180       // all objects that can change their move direction after each step
8181       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8182
8183       if (element != EL_YAMYAM &&
8184           element != EL_DARK_YAMYAM &&
8185           element != EL_PACMAN &&
8186           !(move_pattern & MV_ANY_DIRECTION) &&
8187           move_pattern != MV_TURNING_LEFT &&
8188           move_pattern != MV_TURNING_RIGHT &&
8189           move_pattern != MV_TURNING_LEFT_RIGHT &&
8190           move_pattern != MV_TURNING_RIGHT_LEFT &&
8191           move_pattern != MV_TURNING_RANDOM)
8192       {
8193         TurnRound(x, y);
8194
8195         if (MovDelay[x][y] && (element == EL_BUG ||
8196                                element == EL_SPACESHIP ||
8197                                element == EL_SP_SNIKSNAK ||
8198                                element == EL_SP_ELECTRON ||
8199                                element == EL_MOLE))
8200           TEST_DrawLevelField(x, y);
8201       }
8202     }
8203
8204     if (MovDelay[x][y])         // wait some time before next movement
8205     {
8206       MovDelay[x][y]--;
8207
8208       if (element == EL_ROBOT ||
8209           element == EL_YAMYAM ||
8210           element == EL_DARK_YAMYAM)
8211       {
8212         DrawLevelElementAnimationIfNeeded(x, y, element);
8213         PlayLevelSoundAction(x, y, ACTION_WAITING);
8214       }
8215       else if (element == EL_SP_ELECTRON)
8216         DrawLevelElementAnimationIfNeeded(x, y, element);
8217       else if (element == EL_DRAGON)
8218       {
8219         int i;
8220         int dir = MovDir[x][y];
8221         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8222         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8223         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8224                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8225                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8226                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8227         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8228
8229         GfxAction[x][y] = ACTION_ATTACKING;
8230
8231         if (IS_PLAYER(x, y))
8232           DrawPlayerField(x, y);
8233         else
8234           TEST_DrawLevelField(x, y);
8235
8236         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8237
8238         for (i = 1; i <= 3; i++)
8239         {
8240           int xx = x + i * dx;
8241           int yy = y + i * dy;
8242           int sx = SCREENX(xx);
8243           int sy = SCREENY(yy);
8244           int flame_graphic = graphic + (i - 1);
8245
8246           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8247             break;
8248
8249           if (MovDelay[x][y])
8250           {
8251             int flamed = MovingOrBlocked2Element(xx, yy);
8252
8253             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8254               Bang(xx, yy);
8255             else
8256               RemoveMovingField(xx, yy);
8257
8258             ChangeDelay[xx][yy] = 0;
8259
8260             Tile[xx][yy] = EL_FLAMES;
8261
8262             if (IN_SCR_FIELD(sx, sy))
8263             {
8264               TEST_DrawLevelFieldCrumbled(xx, yy);
8265               DrawGraphic(sx, sy, flame_graphic, frame);
8266             }
8267           }
8268           else
8269           {
8270             if (Tile[xx][yy] == EL_FLAMES)
8271               Tile[xx][yy] = EL_EMPTY;
8272             TEST_DrawLevelField(xx, yy);
8273           }
8274         }
8275       }
8276
8277       if (MovDelay[x][y])       // element still has to wait some time
8278       {
8279         PlayLevelSoundAction(x, y, ACTION_WAITING);
8280
8281         return;
8282       }
8283     }
8284
8285     // now make next step
8286
8287     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8288
8289     if (DONT_COLLIDE_WITH(element) &&
8290         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8291         !PLAYER_ENEMY_PROTECTED(newx, newy))
8292     {
8293       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8294
8295       return;
8296     }
8297
8298     else if (CAN_MOVE_INTO_ACID(element) &&
8299              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8300              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8301              (MovDir[x][y] == MV_DOWN ||
8302               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8303     {
8304       SplashAcid(newx, newy);
8305       Store[x][y] = EL_ACID;
8306     }
8307     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8308     {
8309       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8310           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8311           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8312           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8313       {
8314         RemoveField(x, y);
8315         TEST_DrawLevelField(x, y);
8316
8317         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8318         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8319           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8320
8321         game.friends_still_needed--;
8322         if (!game.friends_still_needed &&
8323             !game.GameOver &&
8324             game.all_players_gone)
8325           LevelSolved();
8326
8327         return;
8328       }
8329       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8330       {
8331         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8332           TEST_DrawLevelField(newx, newy);
8333         else
8334           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8335       }
8336       else if (!IS_FREE(newx, newy))
8337       {
8338         GfxAction[x][y] = ACTION_WAITING;
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_PIG && IN_LEV_FIELD(newx, newy))
8349     {
8350       if (IS_FOOD_PIG(Tile[newx][newy]))
8351       {
8352         if (IS_MOVING(newx, newy))
8353           RemoveMovingField(newx, newy);
8354         else
8355         {
8356           Tile[newx][newy] = EL_EMPTY;
8357           TEST_DrawLevelField(newx, newy);
8358         }
8359
8360         PlayLevelSound(x, y, SND_PIG_DIGGING);
8361       }
8362       else if (!IS_FREE(newx, newy))
8363       {
8364         if (IS_PLAYER(x, y))
8365           DrawPlayerField(x, y);
8366         else
8367           TEST_DrawLevelField(x, y);
8368
8369         return;
8370       }
8371     }
8372     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8373     {
8374       if (Store[x][y] != EL_EMPTY)
8375       {
8376         boolean can_clone = FALSE;
8377         int xx, yy;
8378
8379         // check if element to clone is still there
8380         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8381         {
8382           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8383           {
8384             can_clone = TRUE;
8385
8386             break;
8387           }
8388         }
8389
8390         // cannot clone or target field not free anymore -- do not clone
8391         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8392           Store[x][y] = EL_EMPTY;
8393       }
8394
8395       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8396       {
8397         if (IS_MV_DIAGONAL(MovDir[x][y]))
8398         {
8399           int diagonal_move_dir = MovDir[x][y];
8400           int stored = Store[x][y];
8401           int change_delay = 8;
8402           int graphic;
8403
8404           // android is moving diagonally
8405
8406           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8407
8408           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8409           GfxElement[x][y] = EL_EMC_ANDROID;
8410           GfxAction[x][y] = ACTION_SHRINKING;
8411           GfxDir[x][y] = diagonal_move_dir;
8412           ChangeDelay[x][y] = change_delay;
8413
8414           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8415                                    GfxDir[x][y]);
8416
8417           DrawLevelGraphicAnimation(x, y, graphic);
8418           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8419
8420           if (Tile[newx][newy] == EL_ACID)
8421           {
8422             SplashAcid(newx, newy);
8423
8424             return;
8425           }
8426
8427           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8428
8429           Store[newx][newy] = EL_EMC_ANDROID;
8430           GfxElement[newx][newy] = EL_EMC_ANDROID;
8431           GfxAction[newx][newy] = ACTION_GROWING;
8432           GfxDir[newx][newy] = diagonal_move_dir;
8433           ChangeDelay[newx][newy] = change_delay;
8434
8435           graphic = el_act_dir2img(GfxElement[newx][newy],
8436                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8437
8438           DrawLevelGraphicAnimation(newx, newy, graphic);
8439           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8440
8441           return;
8442         }
8443         else
8444         {
8445           Tile[newx][newy] = EL_EMPTY;
8446           TEST_DrawLevelField(newx, newy);
8447
8448           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8449         }
8450       }
8451       else if (!IS_FREE(newx, newy))
8452       {
8453         return;
8454       }
8455     }
8456     else if (IS_CUSTOM_ELEMENT(element) &&
8457              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8458     {
8459       if (!DigFieldByCE(newx, newy, element))
8460         return;
8461
8462       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8463       {
8464         RunnerVisit[x][y] = FrameCounter;
8465         PlayerVisit[x][y] /= 8;         // expire player visit path
8466       }
8467     }
8468     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8469     {
8470       if (!IS_FREE(newx, newy))
8471       {
8472         if (IS_PLAYER(x, y))
8473           DrawPlayerField(x, y);
8474         else
8475           TEST_DrawLevelField(x, y);
8476
8477         return;
8478       }
8479       else
8480       {
8481         boolean wanna_flame = !RND(10);
8482         int dx = newx - x, dy = newy - y;
8483         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8484         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8485         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8486                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8487         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8488                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8489
8490         if ((wanna_flame ||
8491              IS_CLASSIC_ENEMY(element1) ||
8492              IS_CLASSIC_ENEMY(element2)) &&
8493             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8494             element1 != EL_FLAMES && element2 != EL_FLAMES)
8495         {
8496           ResetGfxAnimation(x, y);
8497           GfxAction[x][y] = ACTION_ATTACKING;
8498
8499           if (IS_PLAYER(x, y))
8500             DrawPlayerField(x, y);
8501           else
8502             TEST_DrawLevelField(x, y);
8503
8504           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8505
8506           MovDelay[x][y] = 50;
8507
8508           Tile[newx][newy] = EL_FLAMES;
8509           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8510             Tile[newx1][newy1] = EL_FLAMES;
8511           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8512             Tile[newx2][newy2] = EL_FLAMES;
8513
8514           return;
8515         }
8516       }
8517     }
8518     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8519              Tile[newx][newy] == EL_DIAMOND)
8520     {
8521       if (IS_MOVING(newx, newy))
8522         RemoveMovingField(newx, newy);
8523       else
8524       {
8525         Tile[newx][newy] = EL_EMPTY;
8526         TEST_DrawLevelField(newx, newy);
8527       }
8528
8529       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8530     }
8531     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8532              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8533     {
8534       if (AmoebaNr[newx][newy])
8535       {
8536         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8537         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8538             Tile[newx][newy] == EL_BD_AMOEBA)
8539           AmoebaCnt[AmoebaNr[newx][newy]]--;
8540       }
8541
8542       if (IS_MOVING(newx, newy))
8543       {
8544         RemoveMovingField(newx, newy);
8545       }
8546       else
8547       {
8548         Tile[newx][newy] = EL_EMPTY;
8549         TEST_DrawLevelField(newx, newy);
8550       }
8551
8552       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8553     }
8554     else if ((element == EL_PACMAN || element == EL_MOLE)
8555              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8556     {
8557       if (AmoebaNr[newx][newy])
8558       {
8559         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8560         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8561             Tile[newx][newy] == EL_BD_AMOEBA)
8562           AmoebaCnt[AmoebaNr[newx][newy]]--;
8563       }
8564
8565       if (element == EL_MOLE)
8566       {
8567         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8568         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8569
8570         ResetGfxAnimation(x, y);
8571         GfxAction[x][y] = ACTION_DIGGING;
8572         TEST_DrawLevelField(x, y);
8573
8574         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8575
8576         return;                         // wait for shrinking amoeba
8577       }
8578       else      // element == EL_PACMAN
8579       {
8580         Tile[newx][newy] = EL_EMPTY;
8581         TEST_DrawLevelField(newx, newy);
8582         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8583       }
8584     }
8585     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8586              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8587               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8588     {
8589       // wait for shrinking amoeba to completely disappear
8590       return;
8591     }
8592     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8593     {
8594       // object was running against a wall
8595
8596       TurnRound(x, y);
8597
8598       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8599         DrawLevelElementAnimation(x, y, element);
8600
8601       if (DONT_TOUCH(element))
8602         TestIfBadThingTouchesPlayer(x, y);
8603
8604       return;
8605     }
8606
8607     InitMovingField(x, y, MovDir[x][y]);
8608
8609     PlayLevelSoundAction(x, y, ACTION_MOVING);
8610   }
8611
8612   if (MovDir[x][y])
8613     ContinueMoving(x, y);
8614 }
8615
8616 void ContinueMoving(int x, int y)
8617 {
8618   int element = Tile[x][y];
8619   struct ElementInfo *ei = &element_info[element];
8620   int direction = MovDir[x][y];
8621   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8622   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8623   int newx = x + dx, newy = y + dy;
8624   int stored = Store[x][y];
8625   int stored_new = Store[newx][newy];
8626   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8627   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8628   boolean last_line = (newy == lev_fieldy - 1);
8629   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8630
8631   if (pushed_by_player)         // special case: moving object pushed by player
8632   {
8633     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8634   }
8635   else if (use_step_delay)      // special case: moving object has step delay
8636   {
8637     if (!MovDelay[x][y])
8638       MovPos[x][y] += getElementMoveStepsize(x, y);
8639
8640     if (MovDelay[x][y])
8641       MovDelay[x][y]--;
8642     else
8643       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8644
8645     if (MovDelay[x][y])
8646     {
8647       TEST_DrawLevelField(x, y);
8648
8649       return;   // element is still waiting
8650     }
8651   }
8652   else                          // normal case: generically moving object
8653   {
8654     MovPos[x][y] += getElementMoveStepsize(x, y);
8655   }
8656
8657   if (ABS(MovPos[x][y]) < TILEX)
8658   {
8659     TEST_DrawLevelField(x, y);
8660
8661     return;     // element is still moving
8662   }
8663
8664   // element reached destination field
8665
8666   Tile[x][y] = EL_EMPTY;
8667   Tile[newx][newy] = element;
8668   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8669
8670   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8671   {
8672     element = Tile[newx][newy] = EL_ACID;
8673   }
8674   else if (element == EL_MOLE)
8675   {
8676     Tile[x][y] = EL_SAND;
8677
8678     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8679   }
8680   else if (element == EL_QUICKSAND_FILLING)
8681   {
8682     element = Tile[newx][newy] = get_next_element(element);
8683     Store[newx][newy] = Store[x][y];
8684   }
8685   else if (element == EL_QUICKSAND_EMPTYING)
8686   {
8687     Tile[x][y] = get_next_element(element);
8688     element = Tile[newx][newy] = Store[x][y];
8689   }
8690   else if (element == EL_QUICKSAND_FAST_FILLING)
8691   {
8692     element = Tile[newx][newy] = get_next_element(element);
8693     Store[newx][newy] = Store[x][y];
8694   }
8695   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8696   {
8697     Tile[x][y] = get_next_element(element);
8698     element = Tile[newx][newy] = Store[x][y];
8699   }
8700   else if (element == EL_MAGIC_WALL_FILLING)
8701   {
8702     element = Tile[newx][newy] = get_next_element(element);
8703     if (!game.magic_wall_active)
8704       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8705     Store[newx][newy] = Store[x][y];
8706   }
8707   else if (element == EL_MAGIC_WALL_EMPTYING)
8708   {
8709     Tile[x][y] = get_next_element(element);
8710     if (!game.magic_wall_active)
8711       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8712     element = Tile[newx][newy] = Store[x][y];
8713
8714     InitField(newx, newy, FALSE);
8715   }
8716   else if (element == EL_BD_MAGIC_WALL_FILLING)
8717   {
8718     element = Tile[newx][newy] = get_next_element(element);
8719     if (!game.magic_wall_active)
8720       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8721     Store[newx][newy] = Store[x][y];
8722   }
8723   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8724   {
8725     Tile[x][y] = get_next_element(element);
8726     if (!game.magic_wall_active)
8727       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8728     element = Tile[newx][newy] = Store[x][y];
8729
8730     InitField(newx, newy, FALSE);
8731   }
8732   else if (element == EL_DC_MAGIC_WALL_FILLING)
8733   {
8734     element = Tile[newx][newy] = get_next_element(element);
8735     if (!game.magic_wall_active)
8736       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8737     Store[newx][newy] = Store[x][y];
8738   }
8739   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8740   {
8741     Tile[x][y] = get_next_element(element);
8742     if (!game.magic_wall_active)
8743       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8744     element = Tile[newx][newy] = Store[x][y];
8745
8746     InitField(newx, newy, FALSE);
8747   }
8748   else if (element == EL_AMOEBA_DROPPING)
8749   {
8750     Tile[x][y] = get_next_element(element);
8751     element = Tile[newx][newy] = Store[x][y];
8752   }
8753   else if (element == EL_SOKOBAN_OBJECT)
8754   {
8755     if (Back[x][y])
8756       Tile[x][y] = Back[x][y];
8757
8758     if (Back[newx][newy])
8759       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8760
8761     Back[x][y] = Back[newx][newy] = 0;
8762   }
8763
8764   Store[x][y] = EL_EMPTY;
8765   MovPos[x][y] = 0;
8766   MovDir[x][y] = 0;
8767   MovDelay[x][y] = 0;
8768
8769   MovDelay[newx][newy] = 0;
8770
8771   if (CAN_CHANGE_OR_HAS_ACTION(element))
8772   {
8773     // copy element change control values to new field
8774     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8775     ChangePage[newx][newy]  = ChangePage[x][y];
8776     ChangeCount[newx][newy] = ChangeCount[x][y];
8777     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8778   }
8779
8780   CustomValue[newx][newy] = CustomValue[x][y];
8781
8782   ChangeDelay[x][y] = 0;
8783   ChangePage[x][y] = -1;
8784   ChangeCount[x][y] = 0;
8785   ChangeEvent[x][y] = -1;
8786
8787   CustomValue[x][y] = 0;
8788
8789   // copy animation control values to new field
8790   GfxFrame[newx][newy]  = GfxFrame[x][y];
8791   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8792   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8793   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8794
8795   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8796
8797   // some elements can leave other elements behind after moving
8798   if (ei->move_leave_element != EL_EMPTY &&
8799       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8800       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8801   {
8802     int move_leave_element = ei->move_leave_element;
8803
8804     // this makes it possible to leave the removed element again
8805     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8806       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8807
8808     Tile[x][y] = move_leave_element;
8809
8810     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8811       MovDir[x][y] = direction;
8812
8813     InitField(x, y, FALSE);
8814
8815     if (GFX_CRUMBLED(Tile[x][y]))
8816       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8817
8818     if (IS_PLAYER_ELEMENT(move_leave_element))
8819       RelocatePlayer(x, y, move_leave_element);
8820   }
8821
8822   // do this after checking for left-behind element
8823   ResetGfxAnimation(x, y);      // reset animation values for old field
8824
8825   if (!CAN_MOVE(element) ||
8826       (CAN_FALL(element) && direction == MV_DOWN &&
8827        (element == EL_SPRING ||
8828         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8829         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8830     GfxDir[x][y] = MovDir[newx][newy] = 0;
8831
8832   TEST_DrawLevelField(x, y);
8833   TEST_DrawLevelField(newx, newy);
8834
8835   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8836
8837   // prevent pushed element from moving on in pushed direction
8838   if (pushed_by_player && CAN_MOVE(element) &&
8839       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8840       !(element_info[element].move_pattern & direction))
8841     TurnRound(newx, newy);
8842
8843   // prevent elements on conveyor belt from moving on in last direction
8844   if (pushed_by_conveyor && CAN_FALL(element) &&
8845       direction & MV_HORIZONTAL)
8846     MovDir[newx][newy] = 0;
8847
8848   if (!pushed_by_player)
8849   {
8850     int nextx = newx + dx, nexty = newy + dy;
8851     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8852
8853     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8854
8855     if (CAN_FALL(element) && direction == MV_DOWN)
8856       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8857
8858     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8859       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8860
8861     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8862       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8863   }
8864
8865   if (DONT_TOUCH(element))      // object may be nasty to player or others
8866   {
8867     TestIfBadThingTouchesPlayer(newx, newy);
8868     TestIfBadThingTouchesFriend(newx, newy);
8869
8870     if (!IS_CUSTOM_ELEMENT(element))
8871       TestIfBadThingTouchesOtherBadThing(newx, newy);
8872   }
8873   else if (element == EL_PENGUIN)
8874     TestIfFriendTouchesBadThing(newx, newy);
8875
8876   if (DONT_GET_HIT_BY(element))
8877   {
8878     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8879   }
8880
8881   // give the player one last chance (one more frame) to move away
8882   if (CAN_FALL(element) && direction == MV_DOWN &&
8883       (last_line || (!IS_FREE(x, newy + 1) &&
8884                      (!IS_PLAYER(x, newy + 1) ||
8885                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8886     Impact(x, newy);
8887
8888   if (pushed_by_player && !game.use_change_when_pushing_bug)
8889   {
8890     int push_side = MV_DIR_OPPOSITE(direction);
8891     struct PlayerInfo *player = PLAYERINFO(x, y);
8892
8893     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8894                                player->index_bit, push_side);
8895     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8896                                         player->index_bit, push_side);
8897   }
8898
8899   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8900     MovDelay[newx][newy] = 1;
8901
8902   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8903
8904   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8905   TestIfElementHitsCustomElement(newx, newy, direction);
8906   TestIfPlayerTouchesCustomElement(newx, newy);
8907   TestIfElementTouchesCustomElement(newx, newy);
8908
8909   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8910       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8911     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8912                              MV_DIR_OPPOSITE(direction));
8913 }
8914
8915 int AmoebaNeighbourNr(int ax, int ay)
8916 {
8917   int i;
8918   int element = Tile[ax][ay];
8919   int group_nr = 0;
8920   static int xy[4][2] =
8921   {
8922     { 0, -1 },
8923     { -1, 0 },
8924     { +1, 0 },
8925     { 0, +1 }
8926   };
8927
8928   for (i = 0; i < NUM_DIRECTIONS; i++)
8929   {
8930     int x = ax + xy[i][0];
8931     int y = ay + xy[i][1];
8932
8933     if (!IN_LEV_FIELD(x, y))
8934       continue;
8935
8936     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8937       group_nr = AmoebaNr[x][y];
8938   }
8939
8940   return group_nr;
8941 }
8942
8943 static void AmoebaMerge(int ax, int ay)
8944 {
8945   int i, x, y, xx, yy;
8946   int new_group_nr = AmoebaNr[ax][ay];
8947   static int xy[4][2] =
8948   {
8949     { 0, -1 },
8950     { -1, 0 },
8951     { +1, 0 },
8952     { 0, +1 }
8953   };
8954
8955   if (new_group_nr == 0)
8956     return;
8957
8958   for (i = 0; i < NUM_DIRECTIONS; i++)
8959   {
8960     x = ax + xy[i][0];
8961     y = ay + xy[i][1];
8962
8963     if (!IN_LEV_FIELD(x, y))
8964       continue;
8965
8966     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8967          Tile[x][y] == EL_BD_AMOEBA ||
8968          Tile[x][y] == EL_AMOEBA_DEAD) &&
8969         AmoebaNr[x][y] != new_group_nr)
8970     {
8971       int old_group_nr = AmoebaNr[x][y];
8972
8973       if (old_group_nr == 0)
8974         return;
8975
8976       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8977       AmoebaCnt[old_group_nr] = 0;
8978       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8979       AmoebaCnt2[old_group_nr] = 0;
8980
8981       SCAN_PLAYFIELD(xx, yy)
8982       {
8983         if (AmoebaNr[xx][yy] == old_group_nr)
8984           AmoebaNr[xx][yy] = new_group_nr;
8985       }
8986     }
8987   }
8988 }
8989
8990 void AmoebaToDiamond(int ax, int ay)
8991 {
8992   int i, x, y;
8993
8994   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8995   {
8996     int group_nr = AmoebaNr[ax][ay];
8997
8998 #ifdef DEBUG
8999     if (group_nr == 0)
9000     {
9001       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9002       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9003
9004       return;
9005     }
9006 #endif
9007
9008     SCAN_PLAYFIELD(x, y)
9009     {
9010       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9011       {
9012         AmoebaNr[x][y] = 0;
9013         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9014       }
9015     }
9016
9017     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9018                             SND_AMOEBA_TURNING_TO_GEM :
9019                             SND_AMOEBA_TURNING_TO_ROCK));
9020     Bang(ax, ay);
9021   }
9022   else
9023   {
9024     static int xy[4][2] =
9025     {
9026       { 0, -1 },
9027       { -1, 0 },
9028       { +1, 0 },
9029       { 0, +1 }
9030     };
9031
9032     for (i = 0; i < NUM_DIRECTIONS; i++)
9033     {
9034       x = ax + xy[i][0];
9035       y = ay + xy[i][1];
9036
9037       if (!IN_LEV_FIELD(x, y))
9038         continue;
9039
9040       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9041       {
9042         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9043                               SND_AMOEBA_TURNING_TO_GEM :
9044                               SND_AMOEBA_TURNING_TO_ROCK));
9045         Bang(x, y);
9046       }
9047     }
9048   }
9049 }
9050
9051 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9052 {
9053   int x, y;
9054   int group_nr = AmoebaNr[ax][ay];
9055   boolean done = FALSE;
9056
9057 #ifdef DEBUG
9058   if (group_nr == 0)
9059   {
9060     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9061     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9062
9063     return;
9064   }
9065 #endif
9066
9067   SCAN_PLAYFIELD(x, y)
9068   {
9069     if (AmoebaNr[x][y] == group_nr &&
9070         (Tile[x][y] == EL_AMOEBA_DEAD ||
9071          Tile[x][y] == EL_BD_AMOEBA ||
9072          Tile[x][y] == EL_AMOEBA_GROWING))
9073     {
9074       AmoebaNr[x][y] = 0;
9075       Tile[x][y] = new_element;
9076       InitField(x, y, FALSE);
9077       TEST_DrawLevelField(x, y);
9078       done = TRUE;
9079     }
9080   }
9081
9082   if (done)
9083     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9084                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9085                             SND_BD_AMOEBA_TURNING_TO_GEM));
9086 }
9087
9088 static void AmoebaGrowing(int x, int y)
9089 {
9090   static unsigned int sound_delay = 0;
9091   static unsigned int sound_delay_value = 0;
9092
9093   if (!MovDelay[x][y])          // start new growing cycle
9094   {
9095     MovDelay[x][y] = 7;
9096
9097     if (DelayReached(&sound_delay, sound_delay_value))
9098     {
9099       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9100       sound_delay_value = 30;
9101     }
9102   }
9103
9104   if (MovDelay[x][y])           // wait some time before growing bigger
9105   {
9106     MovDelay[x][y]--;
9107     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9108     {
9109       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9110                                            6 - MovDelay[x][y]);
9111
9112       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9113     }
9114
9115     if (!MovDelay[x][y])
9116     {
9117       Tile[x][y] = Store[x][y];
9118       Store[x][y] = 0;
9119       TEST_DrawLevelField(x, y);
9120     }
9121   }
9122 }
9123
9124 static void AmoebaShrinking(int x, int y)
9125 {
9126   static unsigned int sound_delay = 0;
9127   static unsigned int sound_delay_value = 0;
9128
9129   if (!MovDelay[x][y])          // start new shrinking cycle
9130   {
9131     MovDelay[x][y] = 7;
9132
9133     if (DelayReached(&sound_delay, sound_delay_value))
9134       sound_delay_value = 30;
9135   }
9136
9137   if (MovDelay[x][y])           // wait some time before shrinking
9138   {
9139     MovDelay[x][y]--;
9140     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9141     {
9142       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9143                                            6 - MovDelay[x][y]);
9144
9145       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9146     }
9147
9148     if (!MovDelay[x][y])
9149     {
9150       Tile[x][y] = EL_EMPTY;
9151       TEST_DrawLevelField(x, y);
9152
9153       // don't let mole enter this field in this cycle;
9154       // (give priority to objects falling to this field from above)
9155       Stop[x][y] = TRUE;
9156     }
9157   }
9158 }
9159
9160 static void AmoebaReproduce(int ax, int ay)
9161 {
9162   int i;
9163   int element = Tile[ax][ay];
9164   int graphic = el2img(element);
9165   int newax = ax, neway = ay;
9166   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9167   static int xy[4][2] =
9168   {
9169     { 0, -1 },
9170     { -1, 0 },
9171     { +1, 0 },
9172     { 0, +1 }
9173   };
9174
9175   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9176   {
9177     Tile[ax][ay] = EL_AMOEBA_DEAD;
9178     TEST_DrawLevelField(ax, ay);
9179     return;
9180   }
9181
9182   if (IS_ANIMATED(graphic))
9183     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9184
9185   if (!MovDelay[ax][ay])        // start making new amoeba field
9186     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9187
9188   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9189   {
9190     MovDelay[ax][ay]--;
9191     if (MovDelay[ax][ay])
9192       return;
9193   }
9194
9195   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9196   {
9197     int start = RND(4);
9198     int x = ax + xy[start][0];
9199     int y = ay + xy[start][1];
9200
9201     if (!IN_LEV_FIELD(x, y))
9202       return;
9203
9204     if (IS_FREE(x, y) ||
9205         CAN_GROW_INTO(Tile[x][y]) ||
9206         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9207         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9208     {
9209       newax = x;
9210       neway = y;
9211     }
9212
9213     if (newax == ax && neway == ay)
9214       return;
9215   }
9216   else                          // normal or "filled" (BD style) amoeba
9217   {
9218     int start = RND(4);
9219     boolean waiting_for_player = FALSE;
9220
9221     for (i = 0; i < NUM_DIRECTIONS; i++)
9222     {
9223       int j = (start + i) % 4;
9224       int x = ax + xy[j][0];
9225       int y = ay + xy[j][1];
9226
9227       if (!IN_LEV_FIELD(x, y))
9228         continue;
9229
9230       if (IS_FREE(x, y) ||
9231           CAN_GROW_INTO(Tile[x][y]) ||
9232           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9233           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9234       {
9235         newax = x;
9236         neway = y;
9237         break;
9238       }
9239       else if (IS_PLAYER(x, y))
9240         waiting_for_player = TRUE;
9241     }
9242
9243     if (newax == ax && neway == ay)             // amoeba cannot grow
9244     {
9245       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9246       {
9247         Tile[ax][ay] = EL_AMOEBA_DEAD;
9248         TEST_DrawLevelField(ax, ay);
9249         AmoebaCnt[AmoebaNr[ax][ay]]--;
9250
9251         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9252         {
9253           if (element == EL_AMOEBA_FULL)
9254             AmoebaToDiamond(ax, ay);
9255           else if (element == EL_BD_AMOEBA)
9256             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9257         }
9258       }
9259       return;
9260     }
9261     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9262     {
9263       // amoeba gets larger by growing in some direction
9264
9265       int new_group_nr = AmoebaNr[ax][ay];
9266
9267 #ifdef DEBUG
9268   if (new_group_nr == 0)
9269   {
9270     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9271           newax, neway);
9272     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9273
9274     return;
9275   }
9276 #endif
9277
9278       AmoebaNr[newax][neway] = new_group_nr;
9279       AmoebaCnt[new_group_nr]++;
9280       AmoebaCnt2[new_group_nr]++;
9281
9282       // if amoeba touches other amoeba(s) after growing, unify them
9283       AmoebaMerge(newax, neway);
9284
9285       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9286       {
9287         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9288         return;
9289       }
9290     }
9291   }
9292
9293   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9294       (neway == lev_fieldy - 1 && newax != ax))
9295   {
9296     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9297     Store[newax][neway] = element;
9298   }
9299   else if (neway == ay || element == EL_EMC_DRIPPER)
9300   {
9301     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9302
9303     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9304   }
9305   else
9306   {
9307     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9308     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9309     Store[ax][ay] = EL_AMOEBA_DROP;
9310     ContinueMoving(ax, ay);
9311     return;
9312   }
9313
9314   TEST_DrawLevelField(newax, neway);
9315 }
9316
9317 static void Life(int ax, int ay)
9318 {
9319   int x1, y1, x2, y2;
9320   int life_time = 40;
9321   int element = Tile[ax][ay];
9322   int graphic = el2img(element);
9323   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9324                          level.biomaze);
9325   boolean changed = FALSE;
9326
9327   if (IS_ANIMATED(graphic))
9328     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9329
9330   if (Stop[ax][ay])
9331     return;
9332
9333   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9334     MovDelay[ax][ay] = life_time;
9335
9336   if (MovDelay[ax][ay])         // wait some time before next cycle
9337   {
9338     MovDelay[ax][ay]--;
9339     if (MovDelay[ax][ay])
9340       return;
9341   }
9342
9343   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9344   {
9345     int xx = ax+x1, yy = ay+y1;
9346     int old_element = Tile[xx][yy];
9347     int num_neighbours = 0;
9348
9349     if (!IN_LEV_FIELD(xx, yy))
9350       continue;
9351
9352     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9353     {
9354       int x = xx+x2, y = yy+y2;
9355
9356       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9357         continue;
9358
9359       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9360       boolean is_neighbour = FALSE;
9361
9362       if (level.use_life_bugs)
9363         is_neighbour =
9364           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9365            (IS_FREE(x, y)                             &&  Stop[x][y]));
9366       else
9367         is_neighbour =
9368           (Last[x][y] == element || is_player_cell);
9369
9370       if (is_neighbour)
9371         num_neighbours++;
9372     }
9373
9374     boolean is_free = FALSE;
9375
9376     if (level.use_life_bugs)
9377       is_free = (IS_FREE(xx, yy));
9378     else
9379       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9380
9381     if (xx == ax && yy == ay)           // field in the middle
9382     {
9383       if (num_neighbours < life_parameter[0] ||
9384           num_neighbours > life_parameter[1])
9385       {
9386         Tile[xx][yy] = EL_EMPTY;
9387         if (Tile[xx][yy] != old_element)
9388           TEST_DrawLevelField(xx, yy);
9389         Stop[xx][yy] = TRUE;
9390         changed = TRUE;
9391       }
9392     }
9393     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9394     {                                   // free border field
9395       if (num_neighbours >= life_parameter[2] &&
9396           num_neighbours <= life_parameter[3])
9397       {
9398         Tile[xx][yy] = element;
9399         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9400         if (Tile[xx][yy] != old_element)
9401           TEST_DrawLevelField(xx, yy);
9402         Stop[xx][yy] = TRUE;
9403         changed = TRUE;
9404       }
9405     }
9406   }
9407
9408   if (changed)
9409     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9410                    SND_GAME_OF_LIFE_GROWING);
9411 }
9412
9413 static void InitRobotWheel(int x, int y)
9414 {
9415   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9416 }
9417
9418 static void RunRobotWheel(int x, int y)
9419 {
9420   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9421 }
9422
9423 static void StopRobotWheel(int x, int y)
9424 {
9425   if (game.robot_wheel_x == x &&
9426       game.robot_wheel_y == y)
9427   {
9428     game.robot_wheel_x = -1;
9429     game.robot_wheel_y = -1;
9430     game.robot_wheel_active = FALSE;
9431   }
9432 }
9433
9434 static void InitTimegateWheel(int x, int y)
9435 {
9436   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9437 }
9438
9439 static void RunTimegateWheel(int x, int y)
9440 {
9441   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9442 }
9443
9444 static void InitMagicBallDelay(int x, int y)
9445 {
9446   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9447 }
9448
9449 static void ActivateMagicBall(int bx, int by)
9450 {
9451   int x, y;
9452
9453   if (level.ball_random)
9454   {
9455     int pos_border = RND(8);    // select one of the eight border elements
9456     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9457     int xx = pos_content % 3;
9458     int yy = pos_content / 3;
9459
9460     x = bx - 1 + xx;
9461     y = by - 1 + yy;
9462
9463     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9464       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9465   }
9466   else
9467   {
9468     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9469     {
9470       int xx = x - bx + 1;
9471       int yy = y - by + 1;
9472
9473       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9474         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9475     }
9476   }
9477
9478   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9479 }
9480
9481 static void CheckExit(int x, int y)
9482 {
9483   if (game.gems_still_needed > 0 ||
9484       game.sokoban_fields_still_needed > 0 ||
9485       game.sokoban_objects_still_needed > 0 ||
9486       game.lights_still_needed > 0)
9487   {
9488     int element = Tile[x][y];
9489     int graphic = el2img(element);
9490
9491     if (IS_ANIMATED(graphic))
9492       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9493
9494     return;
9495   }
9496
9497   // do not re-open exit door closed after last player
9498   if (game.all_players_gone)
9499     return;
9500
9501   Tile[x][y] = EL_EXIT_OPENING;
9502
9503   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9504 }
9505
9506 static void CheckExitEM(int x, int y)
9507 {
9508   if (game.gems_still_needed > 0 ||
9509       game.sokoban_fields_still_needed > 0 ||
9510       game.sokoban_objects_still_needed > 0 ||
9511       game.lights_still_needed > 0)
9512   {
9513     int element = Tile[x][y];
9514     int graphic = el2img(element);
9515
9516     if (IS_ANIMATED(graphic))
9517       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9518
9519     return;
9520   }
9521
9522   // do not re-open exit door closed after last player
9523   if (game.all_players_gone)
9524     return;
9525
9526   Tile[x][y] = EL_EM_EXIT_OPENING;
9527
9528   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9529 }
9530
9531 static void CheckExitSteel(int x, int y)
9532 {
9533   if (game.gems_still_needed > 0 ||
9534       game.sokoban_fields_still_needed > 0 ||
9535       game.sokoban_objects_still_needed > 0 ||
9536       game.lights_still_needed > 0)
9537   {
9538     int element = Tile[x][y];
9539     int graphic = el2img(element);
9540
9541     if (IS_ANIMATED(graphic))
9542       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9543
9544     return;
9545   }
9546
9547   // do not re-open exit door closed after last player
9548   if (game.all_players_gone)
9549     return;
9550
9551   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9552
9553   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9554 }
9555
9556 static void CheckExitSteelEM(int x, int y)
9557 {
9558   if (game.gems_still_needed > 0 ||
9559       game.sokoban_fields_still_needed > 0 ||
9560       game.sokoban_objects_still_needed > 0 ||
9561       game.lights_still_needed > 0)
9562   {
9563     int element = Tile[x][y];
9564     int graphic = el2img(element);
9565
9566     if (IS_ANIMATED(graphic))
9567       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9568
9569     return;
9570   }
9571
9572   // do not re-open exit door closed after last player
9573   if (game.all_players_gone)
9574     return;
9575
9576   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9577
9578   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9579 }
9580
9581 static void CheckExitSP(int x, int y)
9582 {
9583   if (game.gems_still_needed > 0)
9584   {
9585     int element = Tile[x][y];
9586     int graphic = el2img(element);
9587
9588     if (IS_ANIMATED(graphic))
9589       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9590
9591     return;
9592   }
9593
9594   // do not re-open exit door closed after last player
9595   if (game.all_players_gone)
9596     return;
9597
9598   Tile[x][y] = EL_SP_EXIT_OPENING;
9599
9600   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9601 }
9602
9603 static void CloseAllOpenTimegates(void)
9604 {
9605   int x, y;
9606
9607   SCAN_PLAYFIELD(x, y)
9608   {
9609     int element = Tile[x][y];
9610
9611     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9612     {
9613       Tile[x][y] = EL_TIMEGATE_CLOSING;
9614
9615       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9616     }
9617   }
9618 }
9619
9620 static void DrawTwinkleOnField(int x, int y)
9621 {
9622   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9623     return;
9624
9625   if (Tile[x][y] == EL_BD_DIAMOND)
9626     return;
9627
9628   if (MovDelay[x][y] == 0)      // next animation frame
9629     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9630
9631   if (MovDelay[x][y] != 0)      // wait some time before next frame
9632   {
9633     MovDelay[x][y]--;
9634
9635     DrawLevelElementAnimation(x, y, Tile[x][y]);
9636
9637     if (MovDelay[x][y] != 0)
9638     {
9639       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9640                                            10 - MovDelay[x][y]);
9641
9642       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9643     }
9644   }
9645 }
9646
9647 static void MauerWaechst(int x, int y)
9648 {
9649   int delay = 6;
9650
9651   if (!MovDelay[x][y])          // next animation frame
9652     MovDelay[x][y] = 3 * delay;
9653
9654   if (MovDelay[x][y])           // wait some time before next frame
9655   {
9656     MovDelay[x][y]--;
9657
9658     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9659     {
9660       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9661       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9662
9663       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9664     }
9665
9666     if (!MovDelay[x][y])
9667     {
9668       if (MovDir[x][y] == MV_LEFT)
9669       {
9670         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9671           TEST_DrawLevelField(x - 1, y);
9672       }
9673       else if (MovDir[x][y] == MV_RIGHT)
9674       {
9675         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9676           TEST_DrawLevelField(x + 1, y);
9677       }
9678       else if (MovDir[x][y] == MV_UP)
9679       {
9680         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9681           TEST_DrawLevelField(x, y - 1);
9682       }
9683       else
9684       {
9685         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9686           TEST_DrawLevelField(x, y + 1);
9687       }
9688
9689       Tile[x][y] = Store[x][y];
9690       Store[x][y] = 0;
9691       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9692       TEST_DrawLevelField(x, y);
9693     }
9694   }
9695 }
9696
9697 static void MauerAbleger(int ax, int ay)
9698 {
9699   int element = Tile[ax][ay];
9700   int graphic = el2img(element);
9701   boolean oben_frei = FALSE, unten_frei = FALSE;
9702   boolean links_frei = FALSE, rechts_frei = FALSE;
9703   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9704   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9705   boolean new_wall = FALSE;
9706
9707   if (IS_ANIMATED(graphic))
9708     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9709
9710   if (!MovDelay[ax][ay])        // start building new wall
9711     MovDelay[ax][ay] = 6;
9712
9713   if (MovDelay[ax][ay])         // wait some time before building new wall
9714   {
9715     MovDelay[ax][ay]--;
9716     if (MovDelay[ax][ay])
9717       return;
9718   }
9719
9720   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9721     oben_frei = TRUE;
9722   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9723     unten_frei = TRUE;
9724   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9725     links_frei = TRUE;
9726   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9727     rechts_frei = TRUE;
9728
9729   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9730       element == EL_EXPANDABLE_WALL_ANY)
9731   {
9732     if (oben_frei)
9733     {
9734       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9735       Store[ax][ay-1] = element;
9736       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9737       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9738         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9739                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9740       new_wall = TRUE;
9741     }
9742     if (unten_frei)
9743     {
9744       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9745       Store[ax][ay+1] = element;
9746       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9747       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9748         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9749                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9750       new_wall = TRUE;
9751     }
9752   }
9753
9754   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9755       element == EL_EXPANDABLE_WALL_ANY ||
9756       element == EL_EXPANDABLE_WALL ||
9757       element == EL_BD_EXPANDABLE_WALL)
9758   {
9759     if (links_frei)
9760     {
9761       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9762       Store[ax-1][ay] = element;
9763       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9764       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9765         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9766                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9767       new_wall = TRUE;
9768     }
9769
9770     if (rechts_frei)
9771     {
9772       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9773       Store[ax+1][ay] = element;
9774       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9775       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9776         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9777                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9778       new_wall = TRUE;
9779     }
9780   }
9781
9782   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9783     TEST_DrawLevelField(ax, ay);
9784
9785   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9786     oben_massiv = TRUE;
9787   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9788     unten_massiv = TRUE;
9789   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9790     links_massiv = TRUE;
9791   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9792     rechts_massiv = TRUE;
9793
9794   if (((oben_massiv && unten_massiv) ||
9795        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9796        element == EL_EXPANDABLE_WALL) &&
9797       ((links_massiv && rechts_massiv) ||
9798        element == EL_EXPANDABLE_WALL_VERTICAL))
9799     Tile[ax][ay] = EL_WALL;
9800
9801   if (new_wall)
9802     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9803 }
9804
9805 static void MauerAblegerStahl(int ax, int ay)
9806 {
9807   int element = Tile[ax][ay];
9808   int graphic = el2img(element);
9809   boolean oben_frei = FALSE, unten_frei = FALSE;
9810   boolean links_frei = FALSE, rechts_frei = FALSE;
9811   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9812   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9813   boolean new_wall = FALSE;
9814
9815   if (IS_ANIMATED(graphic))
9816     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9817
9818   if (!MovDelay[ax][ay])        // start building new wall
9819     MovDelay[ax][ay] = 6;
9820
9821   if (MovDelay[ax][ay])         // wait some time before building new wall
9822   {
9823     MovDelay[ax][ay]--;
9824     if (MovDelay[ax][ay])
9825       return;
9826   }
9827
9828   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9829     oben_frei = TRUE;
9830   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9831     unten_frei = TRUE;
9832   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9833     links_frei = TRUE;
9834   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9835     rechts_frei = TRUE;
9836
9837   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9838       element == EL_EXPANDABLE_STEELWALL_ANY)
9839   {
9840     if (oben_frei)
9841     {
9842       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9843       Store[ax][ay-1] = element;
9844       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9845       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9846         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9847                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9848       new_wall = TRUE;
9849     }
9850     if (unten_frei)
9851     {
9852       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9853       Store[ax][ay+1] = element;
9854       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9855       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9856         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9857                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9858       new_wall = TRUE;
9859     }
9860   }
9861
9862   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9863       element == EL_EXPANDABLE_STEELWALL_ANY)
9864   {
9865     if (links_frei)
9866     {
9867       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9868       Store[ax-1][ay] = element;
9869       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9870       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9871         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9872                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9873       new_wall = TRUE;
9874     }
9875
9876     if (rechts_frei)
9877     {
9878       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9879       Store[ax+1][ay] = element;
9880       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9881       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9882         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9883                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9884       new_wall = TRUE;
9885     }
9886   }
9887
9888   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9889     oben_massiv = TRUE;
9890   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9891     unten_massiv = TRUE;
9892   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9893     links_massiv = TRUE;
9894   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9895     rechts_massiv = TRUE;
9896
9897   if (((oben_massiv && unten_massiv) ||
9898        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9899       ((links_massiv && rechts_massiv) ||
9900        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9901     Tile[ax][ay] = EL_STEELWALL;
9902
9903   if (new_wall)
9904     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9905 }
9906
9907 static void CheckForDragon(int x, int y)
9908 {
9909   int i, j;
9910   boolean dragon_found = FALSE;
9911   static int xy[4][2] =
9912   {
9913     { 0, -1 },
9914     { -1, 0 },
9915     { +1, 0 },
9916     { 0, +1 }
9917   };
9918
9919   for (i = 0; i < NUM_DIRECTIONS; i++)
9920   {
9921     for (j = 0; j < 4; j++)
9922     {
9923       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9924
9925       if (IN_LEV_FIELD(xx, yy) &&
9926           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9927       {
9928         if (Tile[xx][yy] == EL_DRAGON)
9929           dragon_found = TRUE;
9930       }
9931       else
9932         break;
9933     }
9934   }
9935
9936   if (!dragon_found)
9937   {
9938     for (i = 0; i < NUM_DIRECTIONS; i++)
9939     {
9940       for (j = 0; j < 3; j++)
9941       {
9942         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9943   
9944         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9945         {
9946           Tile[xx][yy] = EL_EMPTY;
9947           TEST_DrawLevelField(xx, yy);
9948         }
9949         else
9950           break;
9951       }
9952     }
9953   }
9954 }
9955
9956 static void InitBuggyBase(int x, int y)
9957 {
9958   int element = Tile[x][y];
9959   int activating_delay = FRAMES_PER_SECOND / 4;
9960
9961   ChangeDelay[x][y] =
9962     (element == EL_SP_BUGGY_BASE ?
9963      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9964      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9965      activating_delay :
9966      element == EL_SP_BUGGY_BASE_ACTIVE ?
9967      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9968 }
9969
9970 static void WarnBuggyBase(int x, int y)
9971 {
9972   int i;
9973   static int xy[4][2] =
9974   {
9975     { 0, -1 },
9976     { -1, 0 },
9977     { +1, 0 },
9978     { 0, +1 }
9979   };
9980
9981   for (i = 0; i < NUM_DIRECTIONS; i++)
9982   {
9983     int xx = x + xy[i][0];
9984     int yy = y + xy[i][1];
9985
9986     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9987     {
9988       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9989
9990       break;
9991     }
9992   }
9993 }
9994
9995 static void InitTrap(int x, int y)
9996 {
9997   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9998 }
9999
10000 static void ActivateTrap(int x, int y)
10001 {
10002   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10003 }
10004
10005 static void ChangeActiveTrap(int x, int y)
10006 {
10007   int graphic = IMG_TRAP_ACTIVE;
10008
10009   // if new animation frame was drawn, correct crumbled sand border
10010   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10011     TEST_DrawLevelFieldCrumbled(x, y);
10012 }
10013
10014 static int getSpecialActionElement(int element, int number, int base_element)
10015 {
10016   return (element != EL_EMPTY ? element :
10017           number != -1 ? base_element + number - 1 :
10018           EL_EMPTY);
10019 }
10020
10021 static int getModifiedActionNumber(int value_old, int operator, int operand,
10022                                    int value_min, int value_max)
10023 {
10024   int value_new = (operator == CA_MODE_SET      ? operand :
10025                    operator == CA_MODE_ADD      ? value_old + operand :
10026                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10027                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10028                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10029                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10030                    value_old);
10031
10032   return (value_new < value_min ? value_min :
10033           value_new > value_max ? value_max :
10034           value_new);
10035 }
10036
10037 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10038 {
10039   struct ElementInfo *ei = &element_info[element];
10040   struct ElementChangeInfo *change = &ei->change_page[page];
10041   int target_element = change->target_element;
10042   int action_type = change->action_type;
10043   int action_mode = change->action_mode;
10044   int action_arg = change->action_arg;
10045   int action_element = change->action_element;
10046   int i;
10047
10048   if (!change->has_action)
10049     return;
10050
10051   // ---------- determine action paramater values -----------------------------
10052
10053   int level_time_value =
10054     (level.time > 0 ? TimeLeft :
10055      TimePlayed);
10056
10057   int action_arg_element_raw =
10058     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10059      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10060      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10061      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10062      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10063      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10064      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10065      EL_EMPTY);
10066   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10067
10068   int action_arg_direction =
10069     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10070      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10071      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10072      change->actual_trigger_side :
10073      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10074      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10075      MV_NONE);
10076
10077   int action_arg_number_min =
10078     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10079      CA_ARG_MIN);
10080
10081   int action_arg_number_max =
10082     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10083      action_type == CA_SET_LEVEL_GEMS ? 999 :
10084      action_type == CA_SET_LEVEL_TIME ? 9999 :
10085      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10086      action_type == CA_SET_CE_VALUE ? 9999 :
10087      action_type == CA_SET_CE_SCORE ? 9999 :
10088      CA_ARG_MAX);
10089
10090   int action_arg_number_reset =
10091     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10092      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10093      action_type == CA_SET_LEVEL_TIME ? level.time :
10094      action_type == CA_SET_LEVEL_SCORE ? 0 :
10095      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10096      action_type == CA_SET_CE_SCORE ? 0 :
10097      0);
10098
10099   int action_arg_number =
10100     (action_arg <= CA_ARG_MAX ? action_arg :
10101      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10102      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10103      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10104      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10105      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10106      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10107      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10108      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10109      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10110      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10111      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10112      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10113      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10114      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10115      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10116      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10117      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10118      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10119      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10120      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10121      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10122      -1);
10123
10124   int action_arg_number_old =
10125     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10126      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10127      action_type == CA_SET_LEVEL_SCORE ? game.score :
10128      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10129      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10130      0);
10131
10132   int action_arg_number_new =
10133     getModifiedActionNumber(action_arg_number_old,
10134                             action_mode, action_arg_number,
10135                             action_arg_number_min, action_arg_number_max);
10136
10137   int trigger_player_bits =
10138     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10139      change->actual_trigger_player_bits : change->trigger_player);
10140
10141   int action_arg_player_bits =
10142     (action_arg >= CA_ARG_PLAYER_1 &&
10143      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10144      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10145      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10146      PLAYER_BITS_ANY);
10147
10148   // ---------- execute action  -----------------------------------------------
10149
10150   switch (action_type)
10151   {
10152     case CA_NO_ACTION:
10153     {
10154       return;
10155     }
10156
10157     // ---------- level actions  ----------------------------------------------
10158
10159     case CA_RESTART_LEVEL:
10160     {
10161       game.restart_level = TRUE;
10162
10163       break;
10164     }
10165
10166     case CA_SHOW_ENVELOPE:
10167     {
10168       int element = getSpecialActionElement(action_arg_element,
10169                                             action_arg_number, EL_ENVELOPE_1);
10170
10171       if (IS_ENVELOPE(element))
10172         local_player->show_envelope = element;
10173
10174       break;
10175     }
10176
10177     case CA_SET_LEVEL_TIME:
10178     {
10179       if (level.time > 0)       // only modify limited time value
10180       {
10181         TimeLeft = action_arg_number_new;
10182
10183         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10184
10185         DisplayGameControlValues();
10186
10187         if (!TimeLeft && setup.time_limit)
10188           for (i = 0; i < MAX_PLAYERS; i++)
10189             KillPlayer(&stored_player[i]);
10190       }
10191
10192       break;
10193     }
10194
10195     case CA_SET_LEVEL_SCORE:
10196     {
10197       game.score = action_arg_number_new;
10198
10199       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10200
10201       DisplayGameControlValues();
10202
10203       break;
10204     }
10205
10206     case CA_SET_LEVEL_GEMS:
10207     {
10208       game.gems_still_needed = action_arg_number_new;
10209
10210       game.snapshot.collected_item = TRUE;
10211
10212       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10213
10214       DisplayGameControlValues();
10215
10216       break;
10217     }
10218
10219     case CA_SET_LEVEL_WIND:
10220     {
10221       game.wind_direction = action_arg_direction;
10222
10223       break;
10224     }
10225
10226     case CA_SET_LEVEL_RANDOM_SEED:
10227     {
10228       // ensure that setting a new random seed while playing is predictable
10229       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10230
10231       break;
10232     }
10233
10234     // ---------- player actions  ---------------------------------------------
10235
10236     case CA_MOVE_PLAYER:
10237     case CA_MOVE_PLAYER_NEW:
10238     {
10239       // automatically move to the next field in specified direction
10240       for (i = 0; i < MAX_PLAYERS; i++)
10241         if (trigger_player_bits & (1 << i))
10242           if (action_type == CA_MOVE_PLAYER ||
10243               stored_player[i].MovPos == 0)
10244             stored_player[i].programmed_action = action_arg_direction;
10245
10246       break;
10247     }
10248
10249     case CA_EXIT_PLAYER:
10250     {
10251       for (i = 0; i < MAX_PLAYERS; i++)
10252         if (action_arg_player_bits & (1 << i))
10253           ExitPlayer(&stored_player[i]);
10254
10255       if (game.players_still_needed == 0)
10256         LevelSolved();
10257
10258       break;
10259     }
10260
10261     case CA_KILL_PLAYER:
10262     {
10263       for (i = 0; i < MAX_PLAYERS; i++)
10264         if (action_arg_player_bits & (1 << i))
10265           KillPlayer(&stored_player[i]);
10266
10267       break;
10268     }
10269
10270     case CA_SET_PLAYER_KEYS:
10271     {
10272       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10273       int element = getSpecialActionElement(action_arg_element,
10274                                             action_arg_number, EL_KEY_1);
10275
10276       if (IS_KEY(element))
10277       {
10278         for (i = 0; i < MAX_PLAYERS; i++)
10279         {
10280           if (trigger_player_bits & (1 << i))
10281           {
10282             stored_player[i].key[KEY_NR(element)] = key_state;
10283
10284             DrawGameDoorValues();
10285           }
10286         }
10287       }
10288
10289       break;
10290     }
10291
10292     case CA_SET_PLAYER_SPEED:
10293     {
10294       for (i = 0; i < MAX_PLAYERS; i++)
10295       {
10296         if (trigger_player_bits & (1 << i))
10297         {
10298           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10299
10300           if (action_arg == CA_ARG_SPEED_FASTER &&
10301               stored_player[i].cannot_move)
10302           {
10303             action_arg_number = STEPSIZE_VERY_SLOW;
10304           }
10305           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10306                    action_arg == CA_ARG_SPEED_FASTER)
10307           {
10308             action_arg_number = 2;
10309             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10310                            CA_MODE_MULTIPLY);
10311           }
10312           else if (action_arg == CA_ARG_NUMBER_RESET)
10313           {
10314             action_arg_number = level.initial_player_stepsize[i];
10315           }
10316
10317           move_stepsize =
10318             getModifiedActionNumber(move_stepsize,
10319                                     action_mode,
10320                                     action_arg_number,
10321                                     action_arg_number_min,
10322                                     action_arg_number_max);
10323
10324           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10325         }
10326       }
10327
10328       break;
10329     }
10330
10331     case CA_SET_PLAYER_SHIELD:
10332     {
10333       for (i = 0; i < MAX_PLAYERS; i++)
10334       {
10335         if (trigger_player_bits & (1 << i))
10336         {
10337           if (action_arg == CA_ARG_SHIELD_OFF)
10338           {
10339             stored_player[i].shield_normal_time_left = 0;
10340             stored_player[i].shield_deadly_time_left = 0;
10341           }
10342           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10343           {
10344             stored_player[i].shield_normal_time_left = 999999;
10345           }
10346           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10347           {
10348             stored_player[i].shield_normal_time_left = 999999;
10349             stored_player[i].shield_deadly_time_left = 999999;
10350           }
10351         }
10352       }
10353
10354       break;
10355     }
10356
10357     case CA_SET_PLAYER_GRAVITY:
10358     {
10359       for (i = 0; i < MAX_PLAYERS; i++)
10360       {
10361         if (trigger_player_bits & (1 << i))
10362         {
10363           stored_player[i].gravity =
10364             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10365              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10366              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10367              stored_player[i].gravity);
10368         }
10369       }
10370
10371       break;
10372     }
10373
10374     case CA_SET_PLAYER_ARTWORK:
10375     {
10376       for (i = 0; i < MAX_PLAYERS; i++)
10377       {
10378         if (trigger_player_bits & (1 << i))
10379         {
10380           int artwork_element = action_arg_element;
10381
10382           if (action_arg == CA_ARG_ELEMENT_RESET)
10383             artwork_element =
10384               (level.use_artwork_element[i] ? level.artwork_element[i] :
10385                stored_player[i].element_nr);
10386
10387           if (stored_player[i].artwork_element != artwork_element)
10388             stored_player[i].Frame = 0;
10389
10390           stored_player[i].artwork_element = artwork_element;
10391
10392           SetPlayerWaiting(&stored_player[i], FALSE);
10393
10394           // set number of special actions for bored and sleeping animation
10395           stored_player[i].num_special_action_bored =
10396             get_num_special_action(artwork_element,
10397                                    ACTION_BORING_1, ACTION_BORING_LAST);
10398           stored_player[i].num_special_action_sleeping =
10399             get_num_special_action(artwork_element,
10400                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10401         }
10402       }
10403
10404       break;
10405     }
10406
10407     case CA_SET_PLAYER_INVENTORY:
10408     {
10409       for (i = 0; i < MAX_PLAYERS; i++)
10410       {
10411         struct PlayerInfo *player = &stored_player[i];
10412         int j, k;
10413
10414         if (trigger_player_bits & (1 << i))
10415         {
10416           int inventory_element = action_arg_element;
10417
10418           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10419               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10420               action_arg == CA_ARG_ELEMENT_ACTION)
10421           {
10422             int element = inventory_element;
10423             int collect_count = element_info[element].collect_count_initial;
10424
10425             if (!IS_CUSTOM_ELEMENT(element))
10426               collect_count = 1;
10427
10428             if (collect_count == 0)
10429               player->inventory_infinite_element = element;
10430             else
10431               for (k = 0; k < collect_count; k++)
10432                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10433                   player->inventory_element[player->inventory_size++] =
10434                     element;
10435           }
10436           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10437                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10438                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10439           {
10440             if (player->inventory_infinite_element != EL_UNDEFINED &&
10441                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10442                                      action_arg_element_raw))
10443               player->inventory_infinite_element = EL_UNDEFINED;
10444
10445             for (k = 0, j = 0; j < player->inventory_size; j++)
10446             {
10447               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10448                                         action_arg_element_raw))
10449                 player->inventory_element[k++] = player->inventory_element[j];
10450             }
10451
10452             player->inventory_size = k;
10453           }
10454           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10455           {
10456             if (player->inventory_size > 0)
10457             {
10458               for (j = 0; j < player->inventory_size - 1; j++)
10459                 player->inventory_element[j] = player->inventory_element[j + 1];
10460
10461               player->inventory_size--;
10462             }
10463           }
10464           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10465           {
10466             if (player->inventory_size > 0)
10467               player->inventory_size--;
10468           }
10469           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10470           {
10471             player->inventory_infinite_element = EL_UNDEFINED;
10472             player->inventory_size = 0;
10473           }
10474           else if (action_arg == CA_ARG_INVENTORY_RESET)
10475           {
10476             player->inventory_infinite_element = EL_UNDEFINED;
10477             player->inventory_size = 0;
10478
10479             if (level.use_initial_inventory[i])
10480             {
10481               for (j = 0; j < level.initial_inventory_size[i]; j++)
10482               {
10483                 int element = level.initial_inventory_content[i][j];
10484                 int collect_count = element_info[element].collect_count_initial;
10485
10486                 if (!IS_CUSTOM_ELEMENT(element))
10487                   collect_count = 1;
10488
10489                 if (collect_count == 0)
10490                   player->inventory_infinite_element = element;
10491                 else
10492                   for (k = 0; k < collect_count; k++)
10493                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10494                       player->inventory_element[player->inventory_size++] =
10495                         element;
10496               }
10497             }
10498           }
10499         }
10500       }
10501
10502       break;
10503     }
10504
10505     // ---------- CE actions  -------------------------------------------------
10506
10507     case CA_SET_CE_VALUE:
10508     {
10509       int last_ce_value = CustomValue[x][y];
10510
10511       CustomValue[x][y] = action_arg_number_new;
10512
10513       if (CustomValue[x][y] != last_ce_value)
10514       {
10515         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10516         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10517
10518         if (CustomValue[x][y] == 0)
10519         {
10520           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10521           ChangeCount[x][y] = 0;        // allow at least one more change
10522
10523           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10524           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10525         }
10526       }
10527
10528       break;
10529     }
10530
10531     case CA_SET_CE_SCORE:
10532     {
10533       int last_ce_score = ei->collect_score;
10534
10535       ei->collect_score = action_arg_number_new;
10536
10537       if (ei->collect_score != last_ce_score)
10538       {
10539         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10540         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10541
10542         if (ei->collect_score == 0)
10543         {
10544           int xx, yy;
10545
10546           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10547           ChangeCount[x][y] = 0;        // allow at least one more change
10548
10549           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10550           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10551
10552           /*
10553             This is a very special case that seems to be a mixture between
10554             CheckElementChange() and CheckTriggeredElementChange(): while
10555             the first one only affects single elements that are triggered
10556             directly, the second one affects multiple elements in the playfield
10557             that are triggered indirectly by another element. This is a third
10558             case: Changing the CE score always affects multiple identical CEs,
10559             so every affected CE must be checked, not only the single CE for
10560             which the CE score was changed in the first place (as every instance
10561             of that CE shares the same CE score, and therefore also can change)!
10562           */
10563           SCAN_PLAYFIELD(xx, yy)
10564           {
10565             if (Tile[xx][yy] == element)
10566               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10567                                  CE_SCORE_GETS_ZERO);
10568           }
10569         }
10570       }
10571
10572       break;
10573     }
10574
10575     case CA_SET_CE_ARTWORK:
10576     {
10577       int artwork_element = action_arg_element;
10578       boolean reset_frame = FALSE;
10579       int xx, yy;
10580
10581       if (action_arg == CA_ARG_ELEMENT_RESET)
10582         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10583                            element);
10584
10585       if (ei->gfx_element != artwork_element)
10586         reset_frame = TRUE;
10587
10588       ei->gfx_element = artwork_element;
10589
10590       SCAN_PLAYFIELD(xx, yy)
10591       {
10592         if (Tile[xx][yy] == element)
10593         {
10594           if (reset_frame)
10595           {
10596             ResetGfxAnimation(xx, yy);
10597             ResetRandomAnimationValue(xx, yy);
10598           }
10599
10600           TEST_DrawLevelField(xx, yy);
10601         }
10602       }
10603
10604       break;
10605     }
10606
10607     // ---------- engine actions  ---------------------------------------------
10608
10609     case CA_SET_ENGINE_SCAN_MODE:
10610     {
10611       InitPlayfieldScanMode(action_arg);
10612
10613       break;
10614     }
10615
10616     default:
10617       break;
10618   }
10619 }
10620
10621 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10622 {
10623   int old_element = Tile[x][y];
10624   int new_element = GetElementFromGroupElement(element);
10625   int previous_move_direction = MovDir[x][y];
10626   int last_ce_value = CustomValue[x][y];
10627   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10628   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10629   boolean add_player_onto_element = (new_element_is_player &&
10630                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10631                                      IS_WALKABLE(old_element));
10632
10633   if (!add_player_onto_element)
10634   {
10635     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10636       RemoveMovingField(x, y);
10637     else
10638       RemoveField(x, y);
10639
10640     Tile[x][y] = new_element;
10641
10642     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10643       MovDir[x][y] = previous_move_direction;
10644
10645     if (element_info[new_element].use_last_ce_value)
10646       CustomValue[x][y] = last_ce_value;
10647
10648     InitField_WithBug1(x, y, FALSE);
10649
10650     new_element = Tile[x][y];   // element may have changed
10651
10652     ResetGfxAnimation(x, y);
10653     ResetRandomAnimationValue(x, y);
10654
10655     TEST_DrawLevelField(x, y);
10656
10657     if (GFX_CRUMBLED(new_element))
10658       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10659   }
10660
10661   // check if element under the player changes from accessible to unaccessible
10662   // (needed for special case of dropping element which then changes)
10663   // (must be checked after creating new element for walkable group elements)
10664   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10665       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10666   {
10667     Bang(x, y);
10668
10669     return;
10670   }
10671
10672   // "ChangeCount" not set yet to allow "entered by player" change one time
10673   if (new_element_is_player)
10674     RelocatePlayer(x, y, new_element);
10675
10676   if (is_change)
10677     ChangeCount[x][y]++;        // count number of changes in the same frame
10678
10679   TestIfBadThingTouchesPlayer(x, y);
10680   TestIfPlayerTouchesCustomElement(x, y);
10681   TestIfElementTouchesCustomElement(x, y);
10682 }
10683
10684 static void CreateField(int x, int y, int element)
10685 {
10686   CreateFieldExt(x, y, element, FALSE);
10687 }
10688
10689 static void CreateElementFromChange(int x, int y, int element)
10690 {
10691   element = GET_VALID_RUNTIME_ELEMENT(element);
10692
10693   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10694   {
10695     int old_element = Tile[x][y];
10696
10697     // prevent changed element from moving in same engine frame
10698     // unless both old and new element can either fall or move
10699     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10700         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10701       Stop[x][y] = TRUE;
10702   }
10703
10704   CreateFieldExt(x, y, element, TRUE);
10705 }
10706
10707 static boolean ChangeElement(int x, int y, int element, int page)
10708 {
10709   struct ElementInfo *ei = &element_info[element];
10710   struct ElementChangeInfo *change = &ei->change_page[page];
10711   int ce_value = CustomValue[x][y];
10712   int ce_score = ei->collect_score;
10713   int target_element;
10714   int old_element = Tile[x][y];
10715
10716   // always use default change event to prevent running into a loop
10717   if (ChangeEvent[x][y] == -1)
10718     ChangeEvent[x][y] = CE_DELAY;
10719
10720   if (ChangeEvent[x][y] == CE_DELAY)
10721   {
10722     // reset actual trigger element, trigger player and action element
10723     change->actual_trigger_element = EL_EMPTY;
10724     change->actual_trigger_player = EL_EMPTY;
10725     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10726     change->actual_trigger_side = CH_SIDE_NONE;
10727     change->actual_trigger_ce_value = 0;
10728     change->actual_trigger_ce_score = 0;
10729   }
10730
10731   // do not change elements more than a specified maximum number of changes
10732   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10733     return FALSE;
10734
10735   ChangeCount[x][y]++;          // count number of changes in the same frame
10736
10737   if (change->explode)
10738   {
10739     Bang(x, y);
10740
10741     return TRUE;
10742   }
10743
10744   if (change->use_target_content)
10745   {
10746     boolean complete_replace = TRUE;
10747     boolean can_replace[3][3];
10748     int xx, yy;
10749
10750     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10751     {
10752       boolean is_empty;
10753       boolean is_walkable;
10754       boolean is_diggable;
10755       boolean is_collectible;
10756       boolean is_removable;
10757       boolean is_destructible;
10758       int ex = x + xx - 1;
10759       int ey = y + yy - 1;
10760       int content_element = change->target_content.e[xx][yy];
10761       int e;
10762
10763       can_replace[xx][yy] = TRUE;
10764
10765       if (ex == x && ey == y)   // do not check changing element itself
10766         continue;
10767
10768       if (content_element == EL_EMPTY_SPACE)
10769       {
10770         can_replace[xx][yy] = FALSE;    // do not replace border with space
10771
10772         continue;
10773       }
10774
10775       if (!IN_LEV_FIELD(ex, ey))
10776       {
10777         can_replace[xx][yy] = FALSE;
10778         complete_replace = FALSE;
10779
10780         continue;
10781       }
10782
10783       e = Tile[ex][ey];
10784
10785       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10786         e = MovingOrBlocked2Element(ex, ey);
10787
10788       is_empty = (IS_FREE(ex, ey) ||
10789                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10790
10791       is_walkable     = (is_empty || IS_WALKABLE(e));
10792       is_diggable     = (is_empty || IS_DIGGABLE(e));
10793       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10794       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10795       is_removable    = (is_diggable || is_collectible);
10796
10797       can_replace[xx][yy] =
10798         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10799           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10800           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10801           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10802           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10803           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10804          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10805
10806       if (!can_replace[xx][yy])
10807         complete_replace = FALSE;
10808     }
10809
10810     if (!change->only_if_complete || complete_replace)
10811     {
10812       boolean something_has_changed = FALSE;
10813
10814       if (change->only_if_complete && change->use_random_replace &&
10815           RND(100) < change->random_percentage)
10816         return FALSE;
10817
10818       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10819       {
10820         int ex = x + xx - 1;
10821         int ey = y + yy - 1;
10822         int content_element;
10823
10824         if (can_replace[xx][yy] && (!change->use_random_replace ||
10825                                     RND(100) < change->random_percentage))
10826         {
10827           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10828             RemoveMovingField(ex, ey);
10829
10830           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10831
10832           content_element = change->target_content.e[xx][yy];
10833           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10834                                               ce_value, ce_score);
10835
10836           CreateElementFromChange(ex, ey, target_element);
10837
10838           something_has_changed = TRUE;
10839
10840           // for symmetry reasons, freeze newly created border elements
10841           if (ex != x || ey != y)
10842             Stop[ex][ey] = TRUE;        // no more moving in this frame
10843         }
10844       }
10845
10846       if (something_has_changed)
10847       {
10848         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10849         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10850       }
10851     }
10852   }
10853   else
10854   {
10855     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10856                                         ce_value, ce_score);
10857
10858     if (element == EL_DIAGONAL_GROWING ||
10859         element == EL_DIAGONAL_SHRINKING)
10860     {
10861       target_element = Store[x][y];
10862
10863       Store[x][y] = EL_EMPTY;
10864     }
10865
10866     // special case: element changes to player (and may be kept if walkable)
10867     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10868       CreateElementFromChange(x, y, EL_EMPTY);
10869
10870     CreateElementFromChange(x, y, target_element);
10871
10872     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10873     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10874   }
10875
10876   // this uses direct change before indirect change
10877   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10878
10879   return TRUE;
10880 }
10881
10882 static void HandleElementChange(int x, int y, int page)
10883 {
10884   int element = MovingOrBlocked2Element(x, y);
10885   struct ElementInfo *ei = &element_info[element];
10886   struct ElementChangeInfo *change = &ei->change_page[page];
10887   boolean handle_action_before_change = FALSE;
10888
10889 #ifdef DEBUG
10890   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10891       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10892   {
10893     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10894           x, y, element, element_info[element].token_name);
10895     Debug("game:playing:HandleElementChange", "This should never happen!");
10896   }
10897 #endif
10898
10899   // this can happen with classic bombs on walkable, changing elements
10900   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10901   {
10902     return;
10903   }
10904
10905   if (ChangeDelay[x][y] == 0)           // initialize element change
10906   {
10907     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10908
10909     if (change->can_change)
10910     {
10911       // !!! not clear why graphic animation should be reset at all here !!!
10912       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10913       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10914
10915       /*
10916         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10917
10918         When using an animation frame delay of 1 (this only happens with
10919         "sp_zonk.moving.left/right" in the classic graphics), the default
10920         (non-moving) animation shows wrong animation frames (while the
10921         moving animation, like "sp_zonk.moving.left/right", is correct,
10922         so this graphical bug never shows up with the classic graphics).
10923         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10924         be drawn instead of the correct frames 0,1,2,3. This is caused by
10925         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10926         an element change: First when the change delay ("ChangeDelay[][]")
10927         counter has reached zero after decrementing, then a second time in
10928         the next frame (after "GfxFrame[][]" was already incremented) when
10929         "ChangeDelay[][]" is reset to the initial delay value again.
10930
10931         This causes frame 0 to be drawn twice, while the last frame won't
10932         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10933
10934         As some animations may already be cleverly designed around this bug
10935         (at least the "Snake Bite" snake tail animation does this), it cannot
10936         simply be fixed here without breaking such existing animations.
10937         Unfortunately, it cannot easily be detected if a graphics set was
10938         designed "before" or "after" the bug was fixed. As a workaround,
10939         a new graphics set option "game.graphics_engine_version" was added
10940         to be able to specify the game's major release version for which the
10941         graphics set was designed, which can then be used to decide if the
10942         bugfix should be used (version 4 and above) or not (version 3 or
10943         below, or if no version was specified at all, as with old sets).
10944
10945         (The wrong/fixed animation frames can be tested with the test level set
10946         "test_gfxframe" and level "000", which contains a specially prepared
10947         custom element at level position (x/y) == (11/9) which uses the zonk
10948         animation mentioned above. Using "game.graphics_engine_version: 4"
10949         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10950         This can also be seen from the debug output for this test element.)
10951       */
10952
10953       // when a custom element is about to change (for example by change delay),
10954       // do not reset graphic animation when the custom element is moving
10955       if (game.graphics_engine_version < 4 &&
10956           !IS_MOVING(x, y))
10957       {
10958         ResetGfxAnimation(x, y);
10959         ResetRandomAnimationValue(x, y);
10960       }
10961
10962       if (change->pre_change_function)
10963         change->pre_change_function(x, y);
10964     }
10965   }
10966
10967   ChangeDelay[x][y]--;
10968
10969   if (ChangeDelay[x][y] != 0)           // continue element change
10970   {
10971     if (change->can_change)
10972     {
10973       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10974
10975       if (IS_ANIMATED(graphic))
10976         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10977
10978       if (change->change_function)
10979         change->change_function(x, y);
10980     }
10981   }
10982   else                                  // finish element change
10983   {
10984     if (ChangePage[x][y] != -1)         // remember page from delayed change
10985     {
10986       page = ChangePage[x][y];
10987       ChangePage[x][y] = -1;
10988
10989       change = &ei->change_page[page];
10990     }
10991
10992     if (IS_MOVING(x, y))                // never change a running system ;-)
10993     {
10994       ChangeDelay[x][y] = 1;            // try change after next move step
10995       ChangePage[x][y] = page;          // remember page to use for change
10996
10997       return;
10998     }
10999
11000     // special case: set new level random seed before changing element
11001     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11002       handle_action_before_change = TRUE;
11003
11004     if (change->has_action && handle_action_before_change)
11005       ExecuteCustomElementAction(x, y, element, page);
11006
11007     if (change->can_change)
11008     {
11009       if (ChangeElement(x, y, element, page))
11010       {
11011         if (change->post_change_function)
11012           change->post_change_function(x, y);
11013       }
11014     }
11015
11016     if (change->has_action && !handle_action_before_change)
11017       ExecuteCustomElementAction(x, y, element, page);
11018   }
11019 }
11020
11021 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11022                                               int trigger_element,
11023                                               int trigger_event,
11024                                               int trigger_player,
11025                                               int trigger_side,
11026                                               int trigger_page)
11027 {
11028   boolean change_done_any = FALSE;
11029   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11030   int i;
11031
11032   if (!(trigger_events[trigger_element][trigger_event]))
11033     return FALSE;
11034
11035   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11036
11037   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11038   {
11039     int element = EL_CUSTOM_START + i;
11040     boolean change_done = FALSE;
11041     int p;
11042
11043     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11044         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11045       continue;
11046
11047     for (p = 0; p < element_info[element].num_change_pages; p++)
11048     {
11049       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11050
11051       if (change->can_change_or_has_action &&
11052           change->has_event[trigger_event] &&
11053           change->trigger_side & trigger_side &&
11054           change->trigger_player & trigger_player &&
11055           change->trigger_page & trigger_page_bits &&
11056           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11057       {
11058         change->actual_trigger_element = trigger_element;
11059         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11060         change->actual_trigger_player_bits = trigger_player;
11061         change->actual_trigger_side = trigger_side;
11062         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11063         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11064
11065         if ((change->can_change && !change_done) || change->has_action)
11066         {
11067           int x, y;
11068
11069           SCAN_PLAYFIELD(x, y)
11070           {
11071             if (Tile[x][y] == element)
11072             {
11073               if (change->can_change && !change_done)
11074               {
11075                 // if element already changed in this frame, not only prevent
11076                 // another element change (checked in ChangeElement()), but
11077                 // also prevent additional element actions for this element
11078
11079                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11080                     !level.use_action_after_change_bug)
11081                   continue;
11082
11083                 ChangeDelay[x][y] = 1;
11084                 ChangeEvent[x][y] = trigger_event;
11085
11086                 HandleElementChange(x, y, p);
11087               }
11088               else if (change->has_action)
11089               {
11090                 // if element already changed in this frame, not only prevent
11091                 // another element change (checked in ChangeElement()), but
11092                 // also prevent additional element actions for this element
11093
11094                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11095                     !level.use_action_after_change_bug)
11096                   continue;
11097
11098                 ExecuteCustomElementAction(x, y, element, p);
11099                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11100               }
11101             }
11102           }
11103
11104           if (change->can_change)
11105           {
11106             change_done = TRUE;
11107             change_done_any = TRUE;
11108           }
11109         }
11110       }
11111     }
11112   }
11113
11114   RECURSION_LOOP_DETECTION_END();
11115
11116   return change_done_any;
11117 }
11118
11119 static boolean CheckElementChangeExt(int x, int y,
11120                                      int element,
11121                                      int trigger_element,
11122                                      int trigger_event,
11123                                      int trigger_player,
11124                                      int trigger_side)
11125 {
11126   boolean change_done = FALSE;
11127   int p;
11128
11129   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11130       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11131     return FALSE;
11132
11133   if (Tile[x][y] == EL_BLOCKED)
11134   {
11135     Blocked2Moving(x, y, &x, &y);
11136     element = Tile[x][y];
11137   }
11138
11139   // check if element has already changed or is about to change after moving
11140   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11141        Tile[x][y] != element) ||
11142
11143       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11144        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11145         ChangePage[x][y] != -1)))
11146     return FALSE;
11147
11148   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11149
11150   for (p = 0; p < element_info[element].num_change_pages; p++)
11151   {
11152     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11153
11154     /* check trigger element for all events where the element that is checked
11155        for changing interacts with a directly adjacent element -- this is
11156        different to element changes that affect other elements to change on the
11157        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11158     boolean check_trigger_element =
11159       (trigger_event == CE_NEXT_TO_X ||
11160        trigger_event == CE_TOUCHING_X ||
11161        trigger_event == CE_HITTING_X ||
11162        trigger_event == CE_HIT_BY_X ||
11163        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11164
11165     if (change->can_change_or_has_action &&
11166         change->has_event[trigger_event] &&
11167         change->trigger_side & trigger_side &&
11168         change->trigger_player & trigger_player &&
11169         (!check_trigger_element ||
11170          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11171     {
11172       change->actual_trigger_element = trigger_element;
11173       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11174       change->actual_trigger_player_bits = trigger_player;
11175       change->actual_trigger_side = trigger_side;
11176       change->actual_trigger_ce_value = CustomValue[x][y];
11177       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11178
11179       // special case: trigger element not at (x,y) position for some events
11180       if (check_trigger_element)
11181       {
11182         static struct
11183         {
11184           int dx, dy;
11185         } move_xy[] =
11186           {
11187             {  0,  0 },
11188             { -1,  0 },
11189             { +1,  0 },
11190             {  0,  0 },
11191             {  0, -1 },
11192             {  0,  0 }, { 0, 0 }, { 0, 0 },
11193             {  0, +1 }
11194           };
11195
11196         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11197         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11198
11199         change->actual_trigger_ce_value = CustomValue[xx][yy];
11200         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11201       }
11202
11203       if (change->can_change && !change_done)
11204       {
11205         ChangeDelay[x][y] = 1;
11206         ChangeEvent[x][y] = trigger_event;
11207
11208         HandleElementChange(x, y, p);
11209
11210         change_done = TRUE;
11211       }
11212       else if (change->has_action)
11213       {
11214         ExecuteCustomElementAction(x, y, element, p);
11215         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11216       }
11217     }
11218   }
11219
11220   RECURSION_LOOP_DETECTION_END();
11221
11222   return change_done;
11223 }
11224
11225 static void PlayPlayerSound(struct PlayerInfo *player)
11226 {
11227   int jx = player->jx, jy = player->jy;
11228   int sound_element = player->artwork_element;
11229   int last_action = player->last_action_waiting;
11230   int action = player->action_waiting;
11231
11232   if (player->is_waiting)
11233   {
11234     if (action != last_action)
11235       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11236     else
11237       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11238   }
11239   else
11240   {
11241     if (action != last_action)
11242       StopSound(element_info[sound_element].sound[last_action]);
11243
11244     if (last_action == ACTION_SLEEPING)
11245       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11246   }
11247 }
11248
11249 static void PlayAllPlayersSound(void)
11250 {
11251   int i;
11252
11253   for (i = 0; i < MAX_PLAYERS; i++)
11254     if (stored_player[i].active)
11255       PlayPlayerSound(&stored_player[i]);
11256 }
11257
11258 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11259 {
11260   boolean last_waiting = player->is_waiting;
11261   int move_dir = player->MovDir;
11262
11263   player->dir_waiting = move_dir;
11264   player->last_action_waiting = player->action_waiting;
11265
11266   if (is_waiting)
11267   {
11268     if (!last_waiting)          // not waiting -> waiting
11269     {
11270       player->is_waiting = TRUE;
11271
11272       player->frame_counter_bored =
11273         FrameCounter +
11274         game.player_boring_delay_fixed +
11275         GetSimpleRandom(game.player_boring_delay_random);
11276       player->frame_counter_sleeping =
11277         FrameCounter +
11278         game.player_sleeping_delay_fixed +
11279         GetSimpleRandom(game.player_sleeping_delay_random);
11280
11281       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11282     }
11283
11284     if (game.player_sleeping_delay_fixed +
11285         game.player_sleeping_delay_random > 0 &&
11286         player->anim_delay_counter == 0 &&
11287         player->post_delay_counter == 0 &&
11288         FrameCounter >= player->frame_counter_sleeping)
11289       player->is_sleeping = TRUE;
11290     else if (game.player_boring_delay_fixed +
11291              game.player_boring_delay_random > 0 &&
11292              FrameCounter >= player->frame_counter_bored)
11293       player->is_bored = TRUE;
11294
11295     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11296                               player->is_bored ? ACTION_BORING :
11297                               ACTION_WAITING);
11298
11299     if (player->is_sleeping && player->use_murphy)
11300     {
11301       // special case for sleeping Murphy when leaning against non-free tile
11302
11303       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11304           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11305            !IS_MOVING(player->jx - 1, player->jy)))
11306         move_dir = MV_LEFT;
11307       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11308                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11309                 !IS_MOVING(player->jx + 1, player->jy)))
11310         move_dir = MV_RIGHT;
11311       else
11312         player->is_sleeping = FALSE;
11313
11314       player->dir_waiting = move_dir;
11315     }
11316
11317     if (player->is_sleeping)
11318     {
11319       if (player->num_special_action_sleeping > 0)
11320       {
11321         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11322         {
11323           int last_special_action = player->special_action_sleeping;
11324           int num_special_action = player->num_special_action_sleeping;
11325           int special_action =
11326             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11327              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11328              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11329              last_special_action + 1 : ACTION_SLEEPING);
11330           int special_graphic =
11331             el_act_dir2img(player->artwork_element, special_action, move_dir);
11332
11333           player->anim_delay_counter =
11334             graphic_info[special_graphic].anim_delay_fixed +
11335             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11336           player->post_delay_counter =
11337             graphic_info[special_graphic].post_delay_fixed +
11338             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11339
11340           player->special_action_sleeping = special_action;
11341         }
11342
11343         if (player->anim_delay_counter > 0)
11344         {
11345           player->action_waiting = player->special_action_sleeping;
11346           player->anim_delay_counter--;
11347         }
11348         else if (player->post_delay_counter > 0)
11349         {
11350           player->post_delay_counter--;
11351         }
11352       }
11353     }
11354     else if (player->is_bored)
11355     {
11356       if (player->num_special_action_bored > 0)
11357       {
11358         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11359         {
11360           int special_action =
11361             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11362           int special_graphic =
11363             el_act_dir2img(player->artwork_element, special_action, move_dir);
11364
11365           player->anim_delay_counter =
11366             graphic_info[special_graphic].anim_delay_fixed +
11367             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11368           player->post_delay_counter =
11369             graphic_info[special_graphic].post_delay_fixed +
11370             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11371
11372           player->special_action_bored = special_action;
11373         }
11374
11375         if (player->anim_delay_counter > 0)
11376         {
11377           player->action_waiting = player->special_action_bored;
11378           player->anim_delay_counter--;
11379         }
11380         else if (player->post_delay_counter > 0)
11381         {
11382           player->post_delay_counter--;
11383         }
11384       }
11385     }
11386   }
11387   else if (last_waiting)        // waiting -> not waiting
11388   {
11389     player->is_waiting = FALSE;
11390     player->is_bored = FALSE;
11391     player->is_sleeping = FALSE;
11392
11393     player->frame_counter_bored = -1;
11394     player->frame_counter_sleeping = -1;
11395
11396     player->anim_delay_counter = 0;
11397     player->post_delay_counter = 0;
11398
11399     player->dir_waiting = player->MovDir;
11400     player->action_waiting = ACTION_DEFAULT;
11401
11402     player->special_action_bored = ACTION_DEFAULT;
11403     player->special_action_sleeping = ACTION_DEFAULT;
11404   }
11405 }
11406
11407 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11408 {
11409   if ((!player->is_moving  && player->was_moving) ||
11410       (player->MovPos == 0 && player->was_moving) ||
11411       (player->is_snapping && !player->was_snapping) ||
11412       (player->is_dropping && !player->was_dropping))
11413   {
11414     if (!CheckSaveEngineSnapshotToList())
11415       return;
11416
11417     player->was_moving = FALSE;
11418     player->was_snapping = TRUE;
11419     player->was_dropping = TRUE;
11420   }
11421   else
11422   {
11423     if (player->is_moving)
11424       player->was_moving = TRUE;
11425
11426     if (!player->is_snapping)
11427       player->was_snapping = FALSE;
11428
11429     if (!player->is_dropping)
11430       player->was_dropping = FALSE;
11431   }
11432
11433   static struct MouseActionInfo mouse_action_last = { 0 };
11434   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11435   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11436
11437   if (new_released)
11438     CheckSaveEngineSnapshotToList();
11439
11440   mouse_action_last = mouse_action;
11441 }
11442
11443 static void CheckSingleStepMode(struct PlayerInfo *player)
11444 {
11445   if (tape.single_step && tape.recording && !tape.pausing)
11446   {
11447     // as it is called "single step mode", just return to pause mode when the
11448     // player stopped moving after one tile (or never starts moving at all)
11449     // (reverse logic needed here in case single step mode used in team mode)
11450     if (player->is_moving ||
11451         player->is_pushing ||
11452         player->is_dropping_pressed ||
11453         player->effective_mouse_action.button)
11454       game.enter_single_step_mode = FALSE;
11455   }
11456
11457   CheckSaveEngineSnapshot(player);
11458 }
11459
11460 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11461 {
11462   int left      = player_action & JOY_LEFT;
11463   int right     = player_action & JOY_RIGHT;
11464   int up        = player_action & JOY_UP;
11465   int down      = player_action & JOY_DOWN;
11466   int button1   = player_action & JOY_BUTTON_1;
11467   int button2   = player_action & JOY_BUTTON_2;
11468   int dx        = (left ? -1 : right ? 1 : 0);
11469   int dy        = (up   ? -1 : down  ? 1 : 0);
11470
11471   if (!player->active || tape.pausing)
11472     return 0;
11473
11474   if (player_action)
11475   {
11476     if (button1)
11477       SnapField(player, dx, dy);
11478     else
11479     {
11480       if (button2)
11481         DropElement(player);
11482
11483       MovePlayer(player, dx, dy);
11484     }
11485
11486     CheckSingleStepMode(player);
11487
11488     SetPlayerWaiting(player, FALSE);
11489
11490     return player_action;
11491   }
11492   else
11493   {
11494     // no actions for this player (no input at player's configured device)
11495
11496     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11497     SnapField(player, 0, 0);
11498     CheckGravityMovementWhenNotMoving(player);
11499
11500     if (player->MovPos == 0)
11501       SetPlayerWaiting(player, TRUE);
11502
11503     if (player->MovPos == 0)    // needed for tape.playing
11504       player->is_moving = FALSE;
11505
11506     player->is_dropping = FALSE;
11507     player->is_dropping_pressed = FALSE;
11508     player->drop_pressed_delay = 0;
11509
11510     CheckSingleStepMode(player);
11511
11512     return 0;
11513   }
11514 }
11515
11516 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11517                                          byte *tape_action)
11518 {
11519   if (!tape.use_mouse_actions)
11520     return;
11521
11522   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11523   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11524   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11525 }
11526
11527 static void SetTapeActionFromMouseAction(byte *tape_action,
11528                                          struct MouseActionInfo *mouse_action)
11529 {
11530   if (!tape.use_mouse_actions)
11531     return;
11532
11533   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11534   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11535   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11536 }
11537
11538 static void CheckLevelSolved(void)
11539 {
11540   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11541   {
11542     if (game_em.level_solved &&
11543         !game_em.game_over)                             // game won
11544     {
11545       LevelSolved();
11546
11547       game_em.game_over = TRUE;
11548
11549       game.all_players_gone = TRUE;
11550     }
11551
11552     if (game_em.game_over)                              // game lost
11553       game.all_players_gone = TRUE;
11554   }
11555   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11556   {
11557     if (game_sp.level_solved &&
11558         !game_sp.game_over)                             // game won
11559     {
11560       LevelSolved();
11561
11562       game_sp.game_over = TRUE;
11563
11564       game.all_players_gone = TRUE;
11565     }
11566
11567     if (game_sp.game_over)                              // game lost
11568       game.all_players_gone = TRUE;
11569   }
11570   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11571   {
11572     if (game_mm.level_solved &&
11573         !game_mm.game_over)                             // game won
11574     {
11575       LevelSolved();
11576
11577       game_mm.game_over = TRUE;
11578
11579       game.all_players_gone = TRUE;
11580     }
11581
11582     if (game_mm.game_over)                              // game lost
11583       game.all_players_gone = TRUE;
11584   }
11585 }
11586
11587 static void CheckLevelTime(void)
11588 {
11589   int i;
11590
11591   if (TimeFrames >= FRAMES_PER_SECOND)
11592   {
11593     TimeFrames = 0;
11594     TapeTime++;
11595
11596     for (i = 0; i < MAX_PLAYERS; i++)
11597     {
11598       struct PlayerInfo *player = &stored_player[i];
11599
11600       if (SHIELD_ON(player))
11601       {
11602         player->shield_normal_time_left--;
11603
11604         if (player->shield_deadly_time_left > 0)
11605           player->shield_deadly_time_left--;
11606       }
11607     }
11608
11609     if (!game.LevelSolved && !level.use_step_counter)
11610     {
11611       TimePlayed++;
11612
11613       if (TimeLeft > 0)
11614       {
11615         TimeLeft--;
11616
11617         if (TimeLeft <= 10 && setup.time_limit)
11618           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11619
11620         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11621            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11622
11623         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11624
11625         if (!TimeLeft && setup.time_limit)
11626         {
11627           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11628             game_em.lev->killed_out_of_time = TRUE;
11629           else
11630             for (i = 0; i < MAX_PLAYERS; i++)
11631               KillPlayer(&stored_player[i]);
11632         }
11633       }
11634       else if (game.no_time_limit && !game.all_players_gone)
11635       {
11636         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11637       }
11638
11639       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11640     }
11641
11642     if (tape.recording || tape.playing)
11643       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11644   }
11645
11646   if (tape.recording || tape.playing)
11647     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11648
11649   UpdateAndDisplayGameControlValues();
11650 }
11651
11652 void AdvanceFrameAndPlayerCounters(int player_nr)
11653 {
11654   int i;
11655
11656   // advance frame counters (global frame counter and time frame counter)
11657   FrameCounter++;
11658   TimeFrames++;
11659
11660   // advance player counters (counters for move delay, move animation etc.)
11661   for (i = 0; i < MAX_PLAYERS; i++)
11662   {
11663     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11664     int move_delay_value = stored_player[i].move_delay_value;
11665     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11666
11667     if (!advance_player_counters)       // not all players may be affected
11668       continue;
11669
11670     if (move_frames == 0)       // less than one move per game frame
11671     {
11672       int stepsize = TILEX / move_delay_value;
11673       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11674       int count = (stored_player[i].is_moving ?
11675                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11676
11677       if (count % delay == 0)
11678         move_frames = 1;
11679     }
11680
11681     stored_player[i].Frame += move_frames;
11682
11683     if (stored_player[i].MovPos != 0)
11684       stored_player[i].StepFrame += move_frames;
11685
11686     if (stored_player[i].move_delay > 0)
11687       stored_player[i].move_delay--;
11688
11689     // due to bugs in previous versions, counter must count up, not down
11690     if (stored_player[i].push_delay != -1)
11691       stored_player[i].push_delay++;
11692
11693     if (stored_player[i].drop_delay > 0)
11694       stored_player[i].drop_delay--;
11695
11696     if (stored_player[i].is_dropping_pressed)
11697       stored_player[i].drop_pressed_delay++;
11698   }
11699 }
11700
11701 void StartGameActions(boolean init_network_game, boolean record_tape,
11702                       int random_seed)
11703 {
11704   unsigned int new_random_seed = InitRND(random_seed);
11705
11706   if (record_tape)
11707     TapeStartRecording(new_random_seed);
11708
11709   if (init_network_game)
11710   {
11711     SendToServer_LevelFile();
11712     SendToServer_StartPlaying();
11713
11714     return;
11715   }
11716
11717   InitGame();
11718 }
11719
11720 static void GameActionsExt(void)
11721 {
11722 #if 0
11723   static unsigned int game_frame_delay = 0;
11724 #endif
11725   unsigned int game_frame_delay_value;
11726   byte *recorded_player_action;
11727   byte summarized_player_action = 0;
11728   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11729   int i;
11730
11731   // detect endless loops, caused by custom element programming
11732   if (recursion_loop_detected && recursion_loop_depth == 0)
11733   {
11734     char *message = getStringCat3("Internal Error! Element ",
11735                                   EL_NAME(recursion_loop_element),
11736                                   " caused endless loop! Quit the game?");
11737
11738     Warn("element '%s' caused endless loop in game engine",
11739          EL_NAME(recursion_loop_element));
11740
11741     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11742
11743     recursion_loop_detected = FALSE;    // if game should be continued
11744
11745     free(message);
11746
11747     return;
11748   }
11749
11750   if (game.restart_level)
11751     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11752
11753   CheckLevelSolved();
11754
11755   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11756     GameWon();
11757
11758   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11759     TapeStop();
11760
11761   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11762     return;
11763
11764   game_frame_delay_value =
11765     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11766
11767   if (tape.playing && tape.warp_forward && !tape.pausing)
11768     game_frame_delay_value = 0;
11769
11770   SetVideoFrameDelay(game_frame_delay_value);
11771
11772   // (de)activate virtual buttons depending on current game status
11773   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11774   {
11775     if (game.all_players_gone)  // if no players there to be controlled anymore
11776       SetOverlayActive(FALSE);
11777     else if (!tape.playing)     // if game continues after tape stopped playing
11778       SetOverlayActive(TRUE);
11779   }
11780
11781 #if 0
11782 #if 0
11783   // ---------- main game synchronization point ----------
11784
11785   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11786
11787   Debug("game:playing:skip", "skip == %d", skip);
11788
11789 #else
11790   // ---------- main game synchronization point ----------
11791
11792   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11793 #endif
11794 #endif
11795
11796   if (network_playing && !network_player_action_received)
11797   {
11798     // try to get network player actions in time
11799
11800     // last chance to get network player actions without main loop delay
11801     HandleNetworking();
11802
11803     // game was quit by network peer
11804     if (game_status != GAME_MODE_PLAYING)
11805       return;
11806
11807     // check if network player actions still missing and game still running
11808     if (!network_player_action_received && !checkGameEnded())
11809       return;           // failed to get network player actions in time
11810
11811     // do not yet reset "network_player_action_received" (for tape.pausing)
11812   }
11813
11814   if (tape.pausing)
11815     return;
11816
11817   // at this point we know that we really continue executing the game
11818
11819   network_player_action_received = FALSE;
11820
11821   // when playing tape, read previously recorded player input from tape data
11822   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11823
11824   local_player->effective_mouse_action = local_player->mouse_action;
11825
11826   if (recorded_player_action != NULL)
11827     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11828                                  recorded_player_action);
11829
11830   // TapePlayAction() may return NULL when toggling to "pause before death"
11831   if (tape.pausing)
11832     return;
11833
11834   if (tape.set_centered_player)
11835   {
11836     game.centered_player_nr_next = tape.centered_player_nr_next;
11837     game.set_centered_player = TRUE;
11838   }
11839
11840   for (i = 0; i < MAX_PLAYERS; i++)
11841   {
11842     summarized_player_action |= stored_player[i].action;
11843
11844     if (!network_playing && (game.team_mode || tape.playing))
11845       stored_player[i].effective_action = stored_player[i].action;
11846   }
11847
11848   if (network_playing && !checkGameEnded())
11849     SendToServer_MovePlayer(summarized_player_action);
11850
11851   // summarize all actions at local players mapped input device position
11852   // (this allows using different input devices in single player mode)
11853   if (!network.enabled && !game.team_mode)
11854     stored_player[map_player_action[local_player->index_nr]].effective_action =
11855       summarized_player_action;
11856
11857   // summarize all actions at centered player in local team mode
11858   if (tape.recording &&
11859       setup.team_mode && !network.enabled &&
11860       setup.input_on_focus &&
11861       game.centered_player_nr != -1)
11862   {
11863     for (i = 0; i < MAX_PLAYERS; i++)
11864       stored_player[map_player_action[i]].effective_action =
11865         (i == game.centered_player_nr ? summarized_player_action : 0);
11866   }
11867
11868   if (recorded_player_action != NULL)
11869     for (i = 0; i < MAX_PLAYERS; i++)
11870       stored_player[i].effective_action = recorded_player_action[i];
11871
11872   for (i = 0; i < MAX_PLAYERS; i++)
11873   {
11874     tape_action[i] = stored_player[i].effective_action;
11875
11876     /* (this may happen in the RND game engine if a player was not present on
11877        the playfield on level start, but appeared later from a custom element */
11878     if (setup.team_mode &&
11879         tape.recording &&
11880         tape_action[i] &&
11881         !tape.player_participates[i])
11882       tape.player_participates[i] = TRUE;
11883   }
11884
11885   SetTapeActionFromMouseAction(tape_action,
11886                                &local_player->effective_mouse_action);
11887
11888   // only record actions from input devices, but not programmed actions
11889   if (tape.recording)
11890     TapeRecordAction(tape_action);
11891
11892   // remember if game was played (especially after tape stopped playing)
11893   if (!tape.playing && summarized_player_action)
11894     game.GamePlayed = TRUE;
11895
11896 #if USE_NEW_PLAYER_ASSIGNMENTS
11897   // !!! also map player actions in single player mode !!!
11898   // if (game.team_mode)
11899   if (1)
11900   {
11901     byte mapped_action[MAX_PLAYERS];
11902
11903 #if DEBUG_PLAYER_ACTIONS
11904     for (i = 0; i < MAX_PLAYERS; i++)
11905       DebugContinued("", "%d, ", stored_player[i].effective_action);
11906 #endif
11907
11908     for (i = 0; i < MAX_PLAYERS; i++)
11909       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11910
11911     for (i = 0; i < MAX_PLAYERS; i++)
11912       stored_player[i].effective_action = mapped_action[i];
11913
11914 #if DEBUG_PLAYER_ACTIONS
11915     DebugContinued("", "=> ");
11916     for (i = 0; i < MAX_PLAYERS; i++)
11917       DebugContinued("", "%d, ", stored_player[i].effective_action);
11918     DebugContinued("game:playing:player", "\n");
11919 #endif
11920   }
11921 #if DEBUG_PLAYER_ACTIONS
11922   else
11923   {
11924     for (i = 0; i < MAX_PLAYERS; i++)
11925       DebugContinued("", "%d, ", stored_player[i].effective_action);
11926     DebugContinued("game:playing:player", "\n");
11927   }
11928 #endif
11929 #endif
11930
11931   for (i = 0; i < MAX_PLAYERS; i++)
11932   {
11933     // allow engine snapshot in case of changed movement attempt
11934     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11935         (stored_player[i].effective_action & KEY_MOTION))
11936       game.snapshot.changed_action = TRUE;
11937
11938     // allow engine snapshot in case of snapping/dropping attempt
11939     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11940         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11941       game.snapshot.changed_action = TRUE;
11942
11943     game.snapshot.last_action[i] = stored_player[i].effective_action;
11944   }
11945
11946   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11947   {
11948     GameActions_EM_Main();
11949   }
11950   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11951   {
11952     GameActions_SP_Main();
11953   }
11954   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11955   {
11956     GameActions_MM_Main();
11957   }
11958   else
11959   {
11960     GameActions_RND_Main();
11961   }
11962
11963   BlitScreenToBitmap(backbuffer);
11964
11965   CheckLevelSolved();
11966   CheckLevelTime();
11967
11968   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11969
11970   if (global.show_frames_per_second)
11971   {
11972     static unsigned int fps_counter = 0;
11973     static int fps_frames = 0;
11974     unsigned int fps_delay_ms = Counter() - fps_counter;
11975
11976     fps_frames++;
11977
11978     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11979     {
11980       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11981
11982       fps_frames = 0;
11983       fps_counter = Counter();
11984
11985       // always draw FPS to screen after FPS value was updated
11986       redraw_mask |= REDRAW_FPS;
11987     }
11988
11989     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11990     if (GetDrawDeactivationMask() == REDRAW_NONE)
11991       redraw_mask |= REDRAW_FPS;
11992   }
11993 }
11994
11995 static void GameActions_CheckSaveEngineSnapshot(void)
11996 {
11997   if (!game.snapshot.save_snapshot)
11998     return;
11999
12000   // clear flag for saving snapshot _before_ saving snapshot
12001   game.snapshot.save_snapshot = FALSE;
12002
12003   SaveEngineSnapshotToList();
12004 }
12005
12006 void GameActions(void)
12007 {
12008   GameActionsExt();
12009
12010   GameActions_CheckSaveEngineSnapshot();
12011 }
12012
12013 void GameActions_EM_Main(void)
12014 {
12015   byte effective_action[MAX_PLAYERS];
12016   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12017   int i;
12018
12019   for (i = 0; i < MAX_PLAYERS; i++)
12020     effective_action[i] = stored_player[i].effective_action;
12021
12022   GameActions_EM(effective_action, warp_mode);
12023 }
12024
12025 void GameActions_SP_Main(void)
12026 {
12027   byte effective_action[MAX_PLAYERS];
12028   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12029   int i;
12030
12031   for (i = 0; i < MAX_PLAYERS; i++)
12032     effective_action[i] = stored_player[i].effective_action;
12033
12034   GameActions_SP(effective_action, warp_mode);
12035
12036   for (i = 0; i < MAX_PLAYERS; i++)
12037   {
12038     if (stored_player[i].force_dropping)
12039       stored_player[i].action |= KEY_BUTTON_DROP;
12040
12041     stored_player[i].force_dropping = FALSE;
12042   }
12043 }
12044
12045 void GameActions_MM_Main(void)
12046 {
12047   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12048
12049   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12050 }
12051
12052 void GameActions_RND_Main(void)
12053 {
12054   GameActions_RND();
12055 }
12056
12057 void GameActions_RND(void)
12058 {
12059   static struct MouseActionInfo mouse_action_last = { 0 };
12060   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12061   int magic_wall_x = 0, magic_wall_y = 0;
12062   int i, x, y, element, graphic, last_gfx_frame;
12063
12064   InitPlayfieldScanModeVars();
12065
12066   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12067   {
12068     SCAN_PLAYFIELD(x, y)
12069     {
12070       ChangeCount[x][y] = 0;
12071       ChangeEvent[x][y] = -1;
12072     }
12073   }
12074
12075   if (game.set_centered_player)
12076   {
12077     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12078
12079     // switching to "all players" only possible if all players fit to screen
12080     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12081     {
12082       game.centered_player_nr_next = game.centered_player_nr;
12083       game.set_centered_player = FALSE;
12084     }
12085
12086     // do not switch focus to non-existing (or non-active) player
12087     if (game.centered_player_nr_next >= 0 &&
12088         !stored_player[game.centered_player_nr_next].active)
12089     {
12090       game.centered_player_nr_next = game.centered_player_nr;
12091       game.set_centered_player = FALSE;
12092     }
12093   }
12094
12095   if (game.set_centered_player &&
12096       ScreenMovPos == 0)        // screen currently aligned at tile position
12097   {
12098     int sx, sy;
12099
12100     if (game.centered_player_nr_next == -1)
12101     {
12102       setScreenCenteredToAllPlayers(&sx, &sy);
12103     }
12104     else
12105     {
12106       sx = stored_player[game.centered_player_nr_next].jx;
12107       sy = stored_player[game.centered_player_nr_next].jy;
12108     }
12109
12110     game.centered_player_nr = game.centered_player_nr_next;
12111     game.set_centered_player = FALSE;
12112
12113     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12114     DrawGameDoorValues();
12115   }
12116
12117   // check single step mode (set flag and clear again if any player is active)
12118   game.enter_single_step_mode =
12119     (tape.single_step && tape.recording && !tape.pausing);
12120
12121   for (i = 0; i < MAX_PLAYERS; i++)
12122   {
12123     int actual_player_action = stored_player[i].effective_action;
12124
12125 #if 1
12126     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12127        - rnd_equinox_tetrachloride 048
12128        - rnd_equinox_tetrachloride_ii 096
12129        - rnd_emanuel_schmieg 002
12130        - doctor_sloan_ww 001, 020
12131     */
12132     if (stored_player[i].MovPos == 0)
12133       CheckGravityMovement(&stored_player[i]);
12134 #endif
12135
12136     // overwrite programmed action with tape action
12137     if (stored_player[i].programmed_action)
12138       actual_player_action = stored_player[i].programmed_action;
12139
12140     PlayerActions(&stored_player[i], actual_player_action);
12141
12142     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12143   }
12144
12145   // single step pause mode may already have been toggled by "ScrollPlayer()"
12146   if (game.enter_single_step_mode && !tape.pausing)
12147     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12148
12149   ScrollScreen(NULL, SCROLL_GO_ON);
12150
12151   /* for backwards compatibility, the following code emulates a fixed bug that
12152      occured when pushing elements (causing elements that just made their last
12153      pushing step to already (if possible) make their first falling step in the
12154      same game frame, which is bad); this code is also needed to use the famous
12155      "spring push bug" which is used in older levels and might be wanted to be
12156      used also in newer levels, but in this case the buggy pushing code is only
12157      affecting the "spring" element and no other elements */
12158
12159   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12160   {
12161     for (i = 0; i < MAX_PLAYERS; i++)
12162     {
12163       struct PlayerInfo *player = &stored_player[i];
12164       int x = player->jx;
12165       int y = player->jy;
12166
12167       if (player->active && player->is_pushing && player->is_moving &&
12168           IS_MOVING(x, y) &&
12169           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12170            Tile[x][y] == EL_SPRING))
12171       {
12172         ContinueMoving(x, y);
12173
12174         // continue moving after pushing (this is actually a bug)
12175         if (!IS_MOVING(x, y))
12176           Stop[x][y] = FALSE;
12177       }
12178     }
12179   }
12180
12181   SCAN_PLAYFIELD(x, y)
12182   {
12183     Last[x][y] = Tile[x][y];
12184
12185     ChangeCount[x][y] = 0;
12186     ChangeEvent[x][y] = -1;
12187
12188     // this must be handled before main playfield loop
12189     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12190     {
12191       MovDelay[x][y]--;
12192       if (MovDelay[x][y] <= 0)
12193         RemoveField(x, y);
12194     }
12195
12196     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12197     {
12198       MovDelay[x][y]--;
12199       if (MovDelay[x][y] <= 0)
12200       {
12201         int element = Store[x][y];
12202         int move_direction = MovDir[x][y];
12203         int player_index_bit = Store2[x][y];
12204
12205         Store[x][y] = 0;
12206         Store2[x][y] = 0;
12207
12208         RemoveField(x, y);
12209         TEST_DrawLevelField(x, y);
12210
12211         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12212
12213         if (IS_ENVELOPE(element))
12214           local_player->show_envelope = element;
12215       }
12216     }
12217
12218 #if DEBUG
12219     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12220     {
12221       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12222             x, y);
12223       Debug("game:playing:GameActions_RND", "This should never happen!");
12224
12225       ChangePage[x][y] = -1;
12226     }
12227 #endif
12228
12229     Stop[x][y] = FALSE;
12230     if (WasJustMoving[x][y] > 0)
12231       WasJustMoving[x][y]--;
12232     if (WasJustFalling[x][y] > 0)
12233       WasJustFalling[x][y]--;
12234     if (CheckCollision[x][y] > 0)
12235       CheckCollision[x][y]--;
12236     if (CheckImpact[x][y] > 0)
12237       CheckImpact[x][y]--;
12238
12239     GfxFrame[x][y]++;
12240
12241     /* reset finished pushing action (not done in ContinueMoving() to allow
12242        continuous pushing animation for elements with zero push delay) */
12243     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12244     {
12245       ResetGfxAnimation(x, y);
12246       TEST_DrawLevelField(x, y);
12247     }
12248
12249 #if DEBUG
12250     if (IS_BLOCKED(x, y))
12251     {
12252       int oldx, oldy;
12253
12254       Blocked2Moving(x, y, &oldx, &oldy);
12255       if (!IS_MOVING(oldx, oldy))
12256       {
12257         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12258         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12259         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12260         Debug("game:playing:GameActions_RND", "This should never happen!");
12261       }
12262     }
12263 #endif
12264   }
12265
12266   if (mouse_action.button)
12267   {
12268     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12269     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12270
12271     x = mouse_action.lx;
12272     y = mouse_action.ly;
12273     element = Tile[x][y];
12274
12275     if (new_button)
12276     {
12277       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12278       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12279                                          ch_button);
12280     }
12281
12282     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12283     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12284                                        ch_button);
12285   }
12286
12287   SCAN_PLAYFIELD(x, y)
12288   {
12289     element = Tile[x][y];
12290     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12291     last_gfx_frame = GfxFrame[x][y];
12292
12293     ResetGfxFrame(x, y);
12294
12295     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12296       DrawLevelGraphicAnimation(x, y, graphic);
12297
12298     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12299         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12300       ResetRandomAnimationValue(x, y);
12301
12302     SetRandomAnimationValue(x, y);
12303
12304     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12305
12306     if (IS_INACTIVE(element))
12307     {
12308       if (IS_ANIMATED(graphic))
12309         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12310
12311       continue;
12312     }
12313
12314     // this may take place after moving, so 'element' may have changed
12315     if (IS_CHANGING(x, y) &&
12316         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12317     {
12318       int page = element_info[element].event_page_nr[CE_DELAY];
12319
12320       HandleElementChange(x, y, page);
12321
12322       element = Tile[x][y];
12323       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12324     }
12325
12326     CheckNextToConditions(x, y);
12327
12328     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12329     {
12330       StartMoving(x, y);
12331
12332       element = Tile[x][y];
12333       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12334
12335       if (IS_ANIMATED(graphic) &&
12336           !IS_MOVING(x, y) &&
12337           !Stop[x][y])
12338         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12339
12340       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12341         TEST_DrawTwinkleOnField(x, y);
12342     }
12343     else if (element == EL_ACID)
12344     {
12345       if (!Stop[x][y])
12346         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12347     }
12348     else if ((element == EL_EXIT_OPEN ||
12349               element == EL_EM_EXIT_OPEN ||
12350               element == EL_SP_EXIT_OPEN ||
12351               element == EL_STEEL_EXIT_OPEN ||
12352               element == EL_EM_STEEL_EXIT_OPEN ||
12353               element == EL_SP_TERMINAL ||
12354               element == EL_SP_TERMINAL_ACTIVE ||
12355               element == EL_EXTRA_TIME ||
12356               element == EL_SHIELD_NORMAL ||
12357               element == EL_SHIELD_DEADLY) &&
12358              IS_ANIMATED(graphic))
12359       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12360     else if (IS_MOVING(x, y))
12361       ContinueMoving(x, y);
12362     else if (IS_ACTIVE_BOMB(element))
12363       CheckDynamite(x, y);
12364     else if (element == EL_AMOEBA_GROWING)
12365       AmoebaGrowing(x, y);
12366     else if (element == EL_AMOEBA_SHRINKING)
12367       AmoebaShrinking(x, y);
12368
12369 #if !USE_NEW_AMOEBA_CODE
12370     else if (IS_AMOEBALIVE(element))
12371       AmoebaReproduce(x, y);
12372 #endif
12373
12374     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12375       Life(x, y);
12376     else if (element == EL_EXIT_CLOSED)
12377       CheckExit(x, y);
12378     else if (element == EL_EM_EXIT_CLOSED)
12379       CheckExitEM(x, y);
12380     else if (element == EL_STEEL_EXIT_CLOSED)
12381       CheckExitSteel(x, y);
12382     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12383       CheckExitSteelEM(x, y);
12384     else if (element == EL_SP_EXIT_CLOSED)
12385       CheckExitSP(x, y);
12386     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12387              element == EL_EXPANDABLE_STEELWALL_GROWING)
12388       MauerWaechst(x, y);
12389     else if (element == EL_EXPANDABLE_WALL ||
12390              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12391              element == EL_EXPANDABLE_WALL_VERTICAL ||
12392              element == EL_EXPANDABLE_WALL_ANY ||
12393              element == EL_BD_EXPANDABLE_WALL)
12394       MauerAbleger(x, y);
12395     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12396              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12397              element == EL_EXPANDABLE_STEELWALL_ANY)
12398       MauerAblegerStahl(x, y);
12399     else if (element == EL_FLAMES)
12400       CheckForDragon(x, y);
12401     else if (element == EL_EXPLOSION)
12402       ; // drawing of correct explosion animation is handled separately
12403     else if (element == EL_ELEMENT_SNAPPING ||
12404              element == EL_DIAGONAL_SHRINKING ||
12405              element == EL_DIAGONAL_GROWING)
12406     {
12407       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12408
12409       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12410     }
12411     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12412       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12413
12414     if (IS_BELT_ACTIVE(element))
12415       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12416
12417     if (game.magic_wall_active)
12418     {
12419       int jx = local_player->jx, jy = local_player->jy;
12420
12421       // play the element sound at the position nearest to the player
12422       if ((element == EL_MAGIC_WALL_FULL ||
12423            element == EL_MAGIC_WALL_ACTIVE ||
12424            element == EL_MAGIC_WALL_EMPTYING ||
12425            element == EL_BD_MAGIC_WALL_FULL ||
12426            element == EL_BD_MAGIC_WALL_ACTIVE ||
12427            element == EL_BD_MAGIC_WALL_EMPTYING ||
12428            element == EL_DC_MAGIC_WALL_FULL ||
12429            element == EL_DC_MAGIC_WALL_ACTIVE ||
12430            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12431           ABS(x - jx) + ABS(y - jy) <
12432           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12433       {
12434         magic_wall_x = x;
12435         magic_wall_y = y;
12436       }
12437     }
12438   }
12439
12440 #if USE_NEW_AMOEBA_CODE
12441   // new experimental amoeba growth stuff
12442   if (!(FrameCounter % 8))
12443   {
12444     static unsigned int random = 1684108901;
12445
12446     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12447     {
12448       x = RND(lev_fieldx);
12449       y = RND(lev_fieldy);
12450       element = Tile[x][y];
12451
12452       if (!IS_PLAYER(x,y) &&
12453           (element == EL_EMPTY ||
12454            CAN_GROW_INTO(element) ||
12455            element == EL_QUICKSAND_EMPTY ||
12456            element == EL_QUICKSAND_FAST_EMPTY ||
12457            element == EL_ACID_SPLASH_LEFT ||
12458            element == EL_ACID_SPLASH_RIGHT))
12459       {
12460         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12461             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12462             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12463             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12464           Tile[x][y] = EL_AMOEBA_DROP;
12465       }
12466
12467       random = random * 129 + 1;
12468     }
12469   }
12470 #endif
12471
12472   game.explosions_delayed = FALSE;
12473
12474   SCAN_PLAYFIELD(x, y)
12475   {
12476     element = Tile[x][y];
12477
12478     if (ExplodeField[x][y])
12479       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12480     else if (element == EL_EXPLOSION)
12481       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12482
12483     ExplodeField[x][y] = EX_TYPE_NONE;
12484   }
12485
12486   game.explosions_delayed = TRUE;
12487
12488   if (game.magic_wall_active)
12489   {
12490     if (!(game.magic_wall_time_left % 4))
12491     {
12492       int element = Tile[magic_wall_x][magic_wall_y];
12493
12494       if (element == EL_BD_MAGIC_WALL_FULL ||
12495           element == EL_BD_MAGIC_WALL_ACTIVE ||
12496           element == EL_BD_MAGIC_WALL_EMPTYING)
12497         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12498       else if (element == EL_DC_MAGIC_WALL_FULL ||
12499                element == EL_DC_MAGIC_WALL_ACTIVE ||
12500                element == EL_DC_MAGIC_WALL_EMPTYING)
12501         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12502       else
12503         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12504     }
12505
12506     if (game.magic_wall_time_left > 0)
12507     {
12508       game.magic_wall_time_left--;
12509
12510       if (!game.magic_wall_time_left)
12511       {
12512         SCAN_PLAYFIELD(x, y)
12513         {
12514           element = Tile[x][y];
12515
12516           if (element == EL_MAGIC_WALL_ACTIVE ||
12517               element == EL_MAGIC_WALL_FULL)
12518           {
12519             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12520             TEST_DrawLevelField(x, y);
12521           }
12522           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12523                    element == EL_BD_MAGIC_WALL_FULL)
12524           {
12525             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12526             TEST_DrawLevelField(x, y);
12527           }
12528           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12529                    element == EL_DC_MAGIC_WALL_FULL)
12530           {
12531             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12532             TEST_DrawLevelField(x, y);
12533           }
12534         }
12535
12536         game.magic_wall_active = FALSE;
12537       }
12538     }
12539   }
12540
12541   if (game.light_time_left > 0)
12542   {
12543     game.light_time_left--;
12544
12545     if (game.light_time_left == 0)
12546       RedrawAllLightSwitchesAndInvisibleElements();
12547   }
12548
12549   if (game.timegate_time_left > 0)
12550   {
12551     game.timegate_time_left--;
12552
12553     if (game.timegate_time_left == 0)
12554       CloseAllOpenTimegates();
12555   }
12556
12557   if (game.lenses_time_left > 0)
12558   {
12559     game.lenses_time_left--;
12560
12561     if (game.lenses_time_left == 0)
12562       RedrawAllInvisibleElementsForLenses();
12563   }
12564
12565   if (game.magnify_time_left > 0)
12566   {
12567     game.magnify_time_left--;
12568
12569     if (game.magnify_time_left == 0)
12570       RedrawAllInvisibleElementsForMagnifier();
12571   }
12572
12573   for (i = 0; i < MAX_PLAYERS; i++)
12574   {
12575     struct PlayerInfo *player = &stored_player[i];
12576
12577     if (SHIELD_ON(player))
12578     {
12579       if (player->shield_deadly_time_left)
12580         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12581       else if (player->shield_normal_time_left)
12582         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12583     }
12584   }
12585
12586 #if USE_DELAYED_GFX_REDRAW
12587   SCAN_PLAYFIELD(x, y)
12588   {
12589     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12590     {
12591       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12592          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12593
12594       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12595         DrawLevelField(x, y);
12596
12597       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12598         DrawLevelFieldCrumbled(x, y);
12599
12600       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12601         DrawLevelFieldCrumbledNeighbours(x, y);
12602
12603       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12604         DrawTwinkleOnField(x, y);
12605     }
12606
12607     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12608   }
12609 #endif
12610
12611   DrawAllPlayers();
12612   PlayAllPlayersSound();
12613
12614   for (i = 0; i < MAX_PLAYERS; i++)
12615   {
12616     struct PlayerInfo *player = &stored_player[i];
12617
12618     if (player->show_envelope != 0 && (!player->active ||
12619                                        player->MovPos == 0))
12620     {
12621       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12622
12623       player->show_envelope = 0;
12624     }
12625   }
12626
12627   // use random number generator in every frame to make it less predictable
12628   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12629     RND(1);
12630
12631   mouse_action_last = mouse_action;
12632 }
12633
12634 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12635 {
12636   int min_x = x, min_y = y, max_x = x, max_y = y;
12637   int scr_fieldx = getScreenFieldSizeX();
12638   int scr_fieldy = getScreenFieldSizeY();
12639   int i;
12640
12641   for (i = 0; i < MAX_PLAYERS; i++)
12642   {
12643     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12644
12645     if (!stored_player[i].active || &stored_player[i] == player)
12646       continue;
12647
12648     min_x = MIN(min_x, jx);
12649     min_y = MIN(min_y, jy);
12650     max_x = MAX(max_x, jx);
12651     max_y = MAX(max_y, jy);
12652   }
12653
12654   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12655 }
12656
12657 static boolean AllPlayersInVisibleScreen(void)
12658 {
12659   int i;
12660
12661   for (i = 0; i < MAX_PLAYERS; i++)
12662   {
12663     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12664
12665     if (!stored_player[i].active)
12666       continue;
12667
12668     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12669       return FALSE;
12670   }
12671
12672   return TRUE;
12673 }
12674
12675 void ScrollLevel(int dx, int dy)
12676 {
12677   int scroll_offset = 2 * TILEX_VAR;
12678   int x, y;
12679
12680   BlitBitmap(drawto_field, drawto_field,
12681              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12682              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12683              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12684              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12685              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12686              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12687
12688   if (dx != 0)
12689   {
12690     x = (dx == 1 ? BX1 : BX2);
12691     for (y = BY1; y <= BY2; y++)
12692       DrawScreenField(x, y);
12693   }
12694
12695   if (dy != 0)
12696   {
12697     y = (dy == 1 ? BY1 : BY2);
12698     for (x = BX1; x <= BX2; x++)
12699       DrawScreenField(x, y);
12700   }
12701
12702   redraw_mask |= REDRAW_FIELD;
12703 }
12704
12705 static boolean canFallDown(struct PlayerInfo *player)
12706 {
12707   int jx = player->jx, jy = player->jy;
12708
12709   return (IN_LEV_FIELD(jx, jy + 1) &&
12710           (IS_FREE(jx, jy + 1) ||
12711            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12712           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12713           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12714 }
12715
12716 static boolean canPassField(int x, int y, int move_dir)
12717 {
12718   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12719   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12720   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12721   int nextx = x + dx;
12722   int nexty = y + dy;
12723   int element = Tile[x][y];
12724
12725   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12726           !CAN_MOVE(element) &&
12727           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12728           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12729           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12730 }
12731
12732 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12733 {
12734   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12735   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12736   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12737   int newx = x + dx;
12738   int newy = y + dy;
12739
12740   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12741           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12742           (IS_DIGGABLE(Tile[newx][newy]) ||
12743            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12744            canPassField(newx, newy, move_dir)));
12745 }
12746
12747 static void CheckGravityMovement(struct PlayerInfo *player)
12748 {
12749   if (player->gravity && !player->programmed_action)
12750   {
12751     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12752     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12753     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12754     int jx = player->jx, jy = player->jy;
12755     boolean player_is_moving_to_valid_field =
12756       (!player_is_snapping &&
12757        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12758         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12759     boolean player_can_fall_down = canFallDown(player);
12760
12761     if (player_can_fall_down &&
12762         !player_is_moving_to_valid_field)
12763       player->programmed_action = MV_DOWN;
12764   }
12765 }
12766
12767 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12768 {
12769   return CheckGravityMovement(player);
12770
12771   if (player->gravity && !player->programmed_action)
12772   {
12773     int jx = player->jx, jy = player->jy;
12774     boolean field_under_player_is_free =
12775       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12776     boolean player_is_standing_on_valid_field =
12777       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12778        (IS_WALKABLE(Tile[jx][jy]) &&
12779         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12780
12781     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12782       player->programmed_action = MV_DOWN;
12783   }
12784 }
12785
12786 /*
12787   MovePlayerOneStep()
12788   -----------------------------------------------------------------------------
12789   dx, dy:               direction (non-diagonal) to try to move the player to
12790   real_dx, real_dy:     direction as read from input device (can be diagonal)
12791 */
12792
12793 boolean MovePlayerOneStep(struct PlayerInfo *player,
12794                           int dx, int dy, int real_dx, int real_dy)
12795 {
12796   int jx = player->jx, jy = player->jy;
12797   int new_jx = jx + dx, new_jy = jy + dy;
12798   int can_move;
12799   boolean player_can_move = !player->cannot_move;
12800
12801   if (!player->active || (!dx && !dy))
12802     return MP_NO_ACTION;
12803
12804   player->MovDir = (dx < 0 ? MV_LEFT :
12805                     dx > 0 ? MV_RIGHT :
12806                     dy < 0 ? MV_UP :
12807                     dy > 0 ? MV_DOWN :  MV_NONE);
12808
12809   if (!IN_LEV_FIELD(new_jx, new_jy))
12810     return MP_NO_ACTION;
12811
12812   if (!player_can_move)
12813   {
12814     if (player->MovPos == 0)
12815     {
12816       player->is_moving = FALSE;
12817       player->is_digging = FALSE;
12818       player->is_collecting = FALSE;
12819       player->is_snapping = FALSE;
12820       player->is_pushing = FALSE;
12821     }
12822   }
12823
12824   if (!network.enabled && game.centered_player_nr == -1 &&
12825       !AllPlayersInSight(player, new_jx, new_jy))
12826     return MP_NO_ACTION;
12827
12828   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12829   if (can_move != MP_MOVING)
12830     return can_move;
12831
12832   // check if DigField() has caused relocation of the player
12833   if (player->jx != jx || player->jy != jy)
12834     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12835
12836   StorePlayer[jx][jy] = 0;
12837   player->last_jx = jx;
12838   player->last_jy = jy;
12839   player->jx = new_jx;
12840   player->jy = new_jy;
12841   StorePlayer[new_jx][new_jy] = player->element_nr;
12842
12843   if (player->move_delay_value_next != -1)
12844   {
12845     player->move_delay_value = player->move_delay_value_next;
12846     player->move_delay_value_next = -1;
12847   }
12848
12849   player->MovPos =
12850     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12851
12852   player->step_counter++;
12853
12854   PlayerVisit[jx][jy] = FrameCounter;
12855
12856   player->is_moving = TRUE;
12857
12858 #if 1
12859   // should better be called in MovePlayer(), but this breaks some tapes
12860   ScrollPlayer(player, SCROLL_INIT);
12861 #endif
12862
12863   return MP_MOVING;
12864 }
12865
12866 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12867 {
12868   int jx = player->jx, jy = player->jy;
12869   int old_jx = jx, old_jy = jy;
12870   int moved = MP_NO_ACTION;
12871
12872   if (!player->active)
12873     return FALSE;
12874
12875   if (!dx && !dy)
12876   {
12877     if (player->MovPos == 0)
12878     {
12879       player->is_moving = FALSE;
12880       player->is_digging = FALSE;
12881       player->is_collecting = FALSE;
12882       player->is_snapping = FALSE;
12883       player->is_pushing = FALSE;
12884     }
12885
12886     return FALSE;
12887   }
12888
12889   if (player->move_delay > 0)
12890     return FALSE;
12891
12892   player->move_delay = -1;              // set to "uninitialized" value
12893
12894   // store if player is automatically moved to next field
12895   player->is_auto_moving = (player->programmed_action != MV_NONE);
12896
12897   // remove the last programmed player action
12898   player->programmed_action = 0;
12899
12900   if (player->MovPos)
12901   {
12902     // should only happen if pre-1.2 tape recordings are played
12903     // this is only for backward compatibility
12904
12905     int original_move_delay_value = player->move_delay_value;
12906
12907 #if DEBUG
12908     Debug("game:playing:MovePlayer",
12909           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12910           tape.counter);
12911 #endif
12912
12913     // scroll remaining steps with finest movement resolution
12914     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12915
12916     while (player->MovPos)
12917     {
12918       ScrollPlayer(player, SCROLL_GO_ON);
12919       ScrollScreen(NULL, SCROLL_GO_ON);
12920
12921       AdvanceFrameAndPlayerCounters(player->index_nr);
12922
12923       DrawAllPlayers();
12924       BackToFront_WithFrameDelay(0);
12925     }
12926
12927     player->move_delay_value = original_move_delay_value;
12928   }
12929
12930   player->is_active = FALSE;
12931
12932   if (player->last_move_dir & MV_HORIZONTAL)
12933   {
12934     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12935       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12936   }
12937   else
12938   {
12939     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12940       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12941   }
12942
12943   if (!moved && !player->is_active)
12944   {
12945     player->is_moving = FALSE;
12946     player->is_digging = FALSE;
12947     player->is_collecting = FALSE;
12948     player->is_snapping = FALSE;
12949     player->is_pushing = FALSE;
12950   }
12951
12952   jx = player->jx;
12953   jy = player->jy;
12954
12955   if (moved & MP_MOVING && !ScreenMovPos &&
12956       (player->index_nr == game.centered_player_nr ||
12957        game.centered_player_nr == -1))
12958   {
12959     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12960
12961     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12962     {
12963       // actual player has left the screen -- scroll in that direction
12964       if (jx != old_jx)         // player has moved horizontally
12965         scroll_x += (jx - old_jx);
12966       else                      // player has moved vertically
12967         scroll_y += (jy - old_jy);
12968     }
12969     else
12970     {
12971       int offset_raw = game.scroll_delay_value;
12972
12973       if (jx != old_jx)         // player has moved horizontally
12974       {
12975         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12976         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12977         int new_scroll_x = jx - MIDPOSX + offset_x;
12978
12979         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12980             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12981           scroll_x = new_scroll_x;
12982
12983         // don't scroll over playfield boundaries
12984         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12985
12986         // don't scroll more than one field at a time
12987         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12988
12989         // don't scroll against the player's moving direction
12990         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12991             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12992           scroll_x = old_scroll_x;
12993       }
12994       else                      // player has moved vertically
12995       {
12996         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12997         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12998         int new_scroll_y = jy - MIDPOSY + offset_y;
12999
13000         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13001             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13002           scroll_y = new_scroll_y;
13003
13004         // don't scroll over playfield boundaries
13005         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13006
13007         // don't scroll more than one field at a time
13008         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13009
13010         // don't scroll against the player's moving direction
13011         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13012             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13013           scroll_y = old_scroll_y;
13014       }
13015     }
13016
13017     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13018     {
13019       if (!network.enabled && game.centered_player_nr == -1 &&
13020           !AllPlayersInVisibleScreen())
13021       {
13022         scroll_x = old_scroll_x;
13023         scroll_y = old_scroll_y;
13024       }
13025       else
13026       {
13027         ScrollScreen(player, SCROLL_INIT);
13028         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13029       }
13030     }
13031   }
13032
13033   player->StepFrame = 0;
13034
13035   if (moved & MP_MOVING)
13036   {
13037     if (old_jx != jx && old_jy == jy)
13038       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13039     else if (old_jx == jx && old_jy != jy)
13040       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13041
13042     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13043
13044     player->last_move_dir = player->MovDir;
13045     player->is_moving = TRUE;
13046     player->is_snapping = FALSE;
13047     player->is_switching = FALSE;
13048     player->is_dropping = FALSE;
13049     player->is_dropping_pressed = FALSE;
13050     player->drop_pressed_delay = 0;
13051
13052 #if 0
13053     // should better be called here than above, but this breaks some tapes
13054     ScrollPlayer(player, SCROLL_INIT);
13055 #endif
13056   }
13057   else
13058   {
13059     CheckGravityMovementWhenNotMoving(player);
13060
13061     player->is_moving = FALSE;
13062
13063     /* at this point, the player is allowed to move, but cannot move right now
13064        (e.g. because of something blocking the way) -- ensure that the player
13065        is also allowed to move in the next frame (in old versions before 3.1.1,
13066        the player was forced to wait again for eight frames before next try) */
13067
13068     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13069       player->move_delay = 0;   // allow direct movement in the next frame
13070   }
13071
13072   if (player->move_delay == -1)         // not yet initialized by DigField()
13073     player->move_delay = player->move_delay_value;
13074
13075   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13076   {
13077     TestIfPlayerTouchesBadThing(jx, jy);
13078     TestIfPlayerTouchesCustomElement(jx, jy);
13079   }
13080
13081   if (!player->active)
13082     RemovePlayer(player);
13083
13084   return moved;
13085 }
13086
13087 void ScrollPlayer(struct PlayerInfo *player, int mode)
13088 {
13089   int jx = player->jx, jy = player->jy;
13090   int last_jx = player->last_jx, last_jy = player->last_jy;
13091   int move_stepsize = TILEX / player->move_delay_value;
13092
13093   if (!player->active)
13094     return;
13095
13096   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13097     return;
13098
13099   if (mode == SCROLL_INIT)
13100   {
13101     player->actual_frame_counter = FrameCounter;
13102     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13103
13104     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13105         Tile[last_jx][last_jy] == EL_EMPTY)
13106     {
13107       int last_field_block_delay = 0;   // start with no blocking at all
13108       int block_delay_adjustment = player->block_delay_adjustment;
13109
13110       // if player blocks last field, add delay for exactly one move
13111       if (player->block_last_field)
13112       {
13113         last_field_block_delay += player->move_delay_value;
13114
13115         // when blocking enabled, prevent moving up despite gravity
13116         if (player->gravity && player->MovDir == MV_UP)
13117           block_delay_adjustment = -1;
13118       }
13119
13120       // add block delay adjustment (also possible when not blocking)
13121       last_field_block_delay += block_delay_adjustment;
13122
13123       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13124       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13125     }
13126
13127     if (player->MovPos != 0)    // player has not yet reached destination
13128       return;
13129   }
13130   else if (!FrameReached(&player->actual_frame_counter, 1))
13131     return;
13132
13133   if (player->MovPos != 0)
13134   {
13135     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13136     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13137
13138     // before DrawPlayer() to draw correct player graphic for this case
13139     if (player->MovPos == 0)
13140       CheckGravityMovement(player);
13141   }
13142
13143   if (player->MovPos == 0)      // player reached destination field
13144   {
13145     if (player->move_delay_reset_counter > 0)
13146     {
13147       player->move_delay_reset_counter--;
13148
13149       if (player->move_delay_reset_counter == 0)
13150       {
13151         // continue with normal speed after quickly moving through gate
13152         HALVE_PLAYER_SPEED(player);
13153
13154         // be able to make the next move without delay
13155         player->move_delay = 0;
13156       }
13157     }
13158
13159     player->last_jx = jx;
13160     player->last_jy = jy;
13161
13162     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13163         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13164         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13165         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13166         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13167         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13168         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13169         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13170     {
13171       ExitPlayer(player);
13172
13173       if (game.players_still_needed == 0 &&
13174           (game.friends_still_needed == 0 ||
13175            IS_SP_ELEMENT(Tile[jx][jy])))
13176         LevelSolved();
13177     }
13178
13179     // this breaks one level: "machine", level 000
13180     {
13181       int move_direction = player->MovDir;
13182       int enter_side = MV_DIR_OPPOSITE(move_direction);
13183       int leave_side = move_direction;
13184       int old_jx = last_jx;
13185       int old_jy = last_jy;
13186       int old_element = Tile[old_jx][old_jy];
13187       int new_element = Tile[jx][jy];
13188
13189       if (IS_CUSTOM_ELEMENT(old_element))
13190         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13191                                    CE_LEFT_BY_PLAYER,
13192                                    player->index_bit, leave_side);
13193
13194       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13195                                           CE_PLAYER_LEAVES_X,
13196                                           player->index_bit, leave_side);
13197
13198       if (IS_CUSTOM_ELEMENT(new_element))
13199         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13200                                    player->index_bit, enter_side);
13201
13202       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13203                                           CE_PLAYER_ENTERS_X,
13204                                           player->index_bit, enter_side);
13205
13206       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13207                                         CE_MOVE_OF_X, move_direction);
13208     }
13209
13210     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13211     {
13212       TestIfPlayerTouchesBadThing(jx, jy);
13213       TestIfPlayerTouchesCustomElement(jx, jy);
13214
13215       /* needed because pushed element has not yet reached its destination,
13216          so it would trigger a change event at its previous field location */
13217       if (!player->is_pushing)
13218         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13219
13220       if (level.finish_dig_collect &&
13221           (player->is_digging || player->is_collecting))
13222       {
13223         int last_element = player->last_removed_element;
13224         int move_direction = player->MovDir;
13225         int enter_side = MV_DIR_OPPOSITE(move_direction);
13226         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13227                             CE_PLAYER_COLLECTS_X);
13228
13229         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13230                                             player->index_bit, enter_side);
13231
13232         player->last_removed_element = EL_UNDEFINED;
13233       }
13234
13235       if (!player->active)
13236         RemovePlayer(player);
13237     }
13238
13239     if (level.use_step_counter)
13240     {
13241       int i;
13242
13243       TimePlayed++;
13244
13245       if (TimeLeft > 0)
13246       {
13247         TimeLeft--;
13248
13249         if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
13250           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13251
13252         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13253
13254         DisplayGameControlValues();
13255
13256         if (!TimeLeft && setup.time_limit && !game.LevelSolved)
13257           for (i = 0; i < MAX_PLAYERS; i++)
13258             KillPlayer(&stored_player[i]);
13259       }
13260       else if (game.no_time_limit && !game.all_players_gone)
13261       {
13262         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13263
13264         DisplayGameControlValues();
13265       }
13266     }
13267
13268     if (tape.single_step && tape.recording && !tape.pausing &&
13269         !player->programmed_action)
13270       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13271
13272     if (!player->programmed_action)
13273       CheckSaveEngineSnapshot(player);
13274   }
13275 }
13276
13277 void ScrollScreen(struct PlayerInfo *player, int mode)
13278 {
13279   static unsigned int screen_frame_counter = 0;
13280
13281   if (mode == SCROLL_INIT)
13282   {
13283     // set scrolling step size according to actual player's moving speed
13284     ScrollStepSize = TILEX / player->move_delay_value;
13285
13286     screen_frame_counter = FrameCounter;
13287     ScreenMovDir = player->MovDir;
13288     ScreenMovPos = player->MovPos;
13289     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13290     return;
13291   }
13292   else if (!FrameReached(&screen_frame_counter, 1))
13293     return;
13294
13295   if (ScreenMovPos)
13296   {
13297     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13298     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13299     redraw_mask |= REDRAW_FIELD;
13300   }
13301   else
13302     ScreenMovDir = MV_NONE;
13303 }
13304
13305 void CheckNextToConditions(int x, int y)
13306 {
13307   int element = Tile[x][y];
13308
13309   if (IS_PLAYER(x, y))
13310     TestIfPlayerNextToCustomElement(x, y);
13311
13312   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13313       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13314     TestIfElementNextToCustomElement(x, y);
13315 }
13316
13317 void TestIfPlayerNextToCustomElement(int x, int y)
13318 {
13319   static int xy[4][2] =
13320   {
13321     { 0, -1 },
13322     { -1, 0 },
13323     { +1, 0 },
13324     { 0, +1 }
13325   };
13326   static int trigger_sides[4][2] =
13327   {
13328     // center side       border side
13329     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13330     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13331     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13332     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13333   };
13334   int i;
13335
13336   if (!IS_PLAYER(x, y))
13337     return;
13338
13339   struct PlayerInfo *player = PLAYERINFO(x, y);
13340
13341   if (player->is_moving)
13342     return;
13343
13344   for (i = 0; i < NUM_DIRECTIONS; i++)
13345   {
13346     int xx = x + xy[i][0];
13347     int yy = y + xy[i][1];
13348     int border_side = trigger_sides[i][1];
13349     int border_element;
13350
13351     if (!IN_LEV_FIELD(xx, yy))
13352       continue;
13353
13354     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13355       continue;         // center and border element not connected
13356
13357     border_element = Tile[xx][yy];
13358
13359     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13360                                player->index_bit, border_side);
13361     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13362                                         CE_PLAYER_NEXT_TO_X,
13363                                         player->index_bit, border_side);
13364
13365     /* use player element that is initially defined in the level playfield,
13366        not the player element that corresponds to the runtime player number
13367        (example: a level that contains EL_PLAYER_3 as the only player would
13368        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13369
13370     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13371                              CE_NEXT_TO_X, border_side);
13372   }
13373 }
13374
13375 void TestIfPlayerTouchesCustomElement(int x, int y)
13376 {
13377   static int xy[4][2] =
13378   {
13379     { 0, -1 },
13380     { -1, 0 },
13381     { +1, 0 },
13382     { 0, +1 }
13383   };
13384   static int trigger_sides[4][2] =
13385   {
13386     // center side       border side
13387     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13388     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13389     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13390     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13391   };
13392   static int touch_dir[4] =
13393   {
13394     MV_LEFT | MV_RIGHT,
13395     MV_UP   | MV_DOWN,
13396     MV_UP   | MV_DOWN,
13397     MV_LEFT | MV_RIGHT
13398   };
13399   int center_element = Tile[x][y];      // should always be non-moving!
13400   int i;
13401
13402   for (i = 0; i < NUM_DIRECTIONS; i++)
13403   {
13404     int xx = x + xy[i][0];
13405     int yy = y + xy[i][1];
13406     int center_side = trigger_sides[i][0];
13407     int border_side = trigger_sides[i][1];
13408     int border_element;
13409
13410     if (!IN_LEV_FIELD(xx, yy))
13411       continue;
13412
13413     if (IS_PLAYER(x, y))                // player found at center element
13414     {
13415       struct PlayerInfo *player = PLAYERINFO(x, y);
13416
13417       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13418         border_element = Tile[xx][yy];          // may be moving!
13419       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13420         border_element = Tile[xx][yy];
13421       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13422         border_element = MovingOrBlocked2Element(xx, yy);
13423       else
13424         continue;               // center and border element do not touch
13425
13426       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13427                                  player->index_bit, border_side);
13428       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13429                                           CE_PLAYER_TOUCHES_X,
13430                                           player->index_bit, border_side);
13431
13432       {
13433         /* use player element that is initially defined in the level playfield,
13434            not the player element that corresponds to the runtime player number
13435            (example: a level that contains EL_PLAYER_3 as the only player would
13436            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13437         int player_element = PLAYERINFO(x, y)->initial_element;
13438
13439         CheckElementChangeBySide(xx, yy, border_element, player_element,
13440                                  CE_TOUCHING_X, border_side);
13441       }
13442     }
13443     else if (IS_PLAYER(xx, yy))         // player found at border element
13444     {
13445       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13446
13447       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13448       {
13449         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13450           continue;             // center and border element do not touch
13451       }
13452
13453       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13454                                  player->index_bit, center_side);
13455       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13456                                           CE_PLAYER_TOUCHES_X,
13457                                           player->index_bit, center_side);
13458
13459       {
13460         /* use player element that is initially defined in the level playfield,
13461            not the player element that corresponds to the runtime player number
13462            (example: a level that contains EL_PLAYER_3 as the only player would
13463            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13464         int player_element = PLAYERINFO(xx, yy)->initial_element;
13465
13466         CheckElementChangeBySide(x, y, center_element, player_element,
13467                                  CE_TOUCHING_X, center_side);
13468       }
13469
13470       break;
13471     }
13472   }
13473 }
13474
13475 void TestIfElementNextToCustomElement(int x, int y)
13476 {
13477   static int xy[4][2] =
13478   {
13479     { 0, -1 },
13480     { -1, 0 },
13481     { +1, 0 },
13482     { 0, +1 }
13483   };
13484   static int trigger_sides[4][2] =
13485   {
13486     // center side      border side
13487     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13488     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13489     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13490     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13491   };
13492   int center_element = Tile[x][y];      // should always be non-moving!
13493   int i;
13494
13495   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13496     return;
13497
13498   for (i = 0; i < NUM_DIRECTIONS; i++)
13499   {
13500     int xx = x + xy[i][0];
13501     int yy = y + xy[i][1];
13502     int border_side = trigger_sides[i][1];
13503     int border_element;
13504
13505     if (!IN_LEV_FIELD(xx, yy))
13506       continue;
13507
13508     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13509       continue;                 // center and border element not connected
13510
13511     border_element = Tile[xx][yy];
13512
13513     // check for change of center element (but change it only once)
13514     if (CheckElementChangeBySide(x, y, center_element, border_element,
13515                                  CE_NEXT_TO_X, border_side))
13516       break;
13517   }
13518 }
13519
13520 void TestIfElementTouchesCustomElement(int x, int y)
13521 {
13522   static int xy[4][2] =
13523   {
13524     { 0, -1 },
13525     { -1, 0 },
13526     { +1, 0 },
13527     { 0, +1 }
13528   };
13529   static int trigger_sides[4][2] =
13530   {
13531     // center side      border side
13532     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13533     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13534     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13535     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13536   };
13537   static int touch_dir[4] =
13538   {
13539     MV_LEFT | MV_RIGHT,
13540     MV_UP   | MV_DOWN,
13541     MV_UP   | MV_DOWN,
13542     MV_LEFT | MV_RIGHT
13543   };
13544   boolean change_center_element = FALSE;
13545   int center_element = Tile[x][y];      // should always be non-moving!
13546   int border_element_old[NUM_DIRECTIONS];
13547   int i;
13548
13549   for (i = 0; i < NUM_DIRECTIONS; i++)
13550   {
13551     int xx = x + xy[i][0];
13552     int yy = y + xy[i][1];
13553     int border_element;
13554
13555     border_element_old[i] = -1;
13556
13557     if (!IN_LEV_FIELD(xx, yy))
13558       continue;
13559
13560     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13561       border_element = Tile[xx][yy];    // may be moving!
13562     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13563       border_element = Tile[xx][yy];
13564     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13565       border_element = MovingOrBlocked2Element(xx, yy);
13566     else
13567       continue;                 // center and border element do not touch
13568
13569     border_element_old[i] = border_element;
13570   }
13571
13572   for (i = 0; i < NUM_DIRECTIONS; i++)
13573   {
13574     int xx = x + xy[i][0];
13575     int yy = y + xy[i][1];
13576     int center_side = trigger_sides[i][0];
13577     int border_element = border_element_old[i];
13578
13579     if (border_element == -1)
13580       continue;
13581
13582     // check for change of border element
13583     CheckElementChangeBySide(xx, yy, border_element, center_element,
13584                              CE_TOUCHING_X, center_side);
13585
13586     // (center element cannot be player, so we dont have to check this here)
13587   }
13588
13589   for (i = 0; i < NUM_DIRECTIONS; i++)
13590   {
13591     int xx = x + xy[i][0];
13592     int yy = y + xy[i][1];
13593     int border_side = trigger_sides[i][1];
13594     int border_element = border_element_old[i];
13595
13596     if (border_element == -1)
13597       continue;
13598
13599     // check for change of center element (but change it only once)
13600     if (!change_center_element)
13601       change_center_element =
13602         CheckElementChangeBySide(x, y, center_element, border_element,
13603                                  CE_TOUCHING_X, border_side);
13604
13605     if (IS_PLAYER(xx, yy))
13606     {
13607       /* use player element that is initially defined in the level playfield,
13608          not the player element that corresponds to the runtime player number
13609          (example: a level that contains EL_PLAYER_3 as the only player would
13610          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13611       int player_element = PLAYERINFO(xx, yy)->initial_element;
13612
13613       CheckElementChangeBySide(x, y, center_element, player_element,
13614                                CE_TOUCHING_X, border_side);
13615     }
13616   }
13617 }
13618
13619 void TestIfElementHitsCustomElement(int x, int y, int direction)
13620 {
13621   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13622   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13623   int hitx = x + dx, hity = y + dy;
13624   int hitting_element = Tile[x][y];
13625   int touched_element;
13626
13627   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13628     return;
13629
13630   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13631                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13632
13633   if (IN_LEV_FIELD(hitx, hity))
13634   {
13635     int opposite_direction = MV_DIR_OPPOSITE(direction);
13636     int hitting_side = direction;
13637     int touched_side = opposite_direction;
13638     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13639                           MovDir[hitx][hity] != direction ||
13640                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13641
13642     object_hit = TRUE;
13643
13644     if (object_hit)
13645     {
13646       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13647                                CE_HITTING_X, touched_side);
13648
13649       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13650                                CE_HIT_BY_X, hitting_side);
13651
13652       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13653                                CE_HIT_BY_SOMETHING, opposite_direction);
13654
13655       if (IS_PLAYER(hitx, hity))
13656       {
13657         /* use player element that is initially defined in the level playfield,
13658            not the player element that corresponds to the runtime player number
13659            (example: a level that contains EL_PLAYER_3 as the only player would
13660            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13661         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13662
13663         CheckElementChangeBySide(x, y, hitting_element, player_element,
13664                                  CE_HITTING_X, touched_side);
13665       }
13666     }
13667   }
13668
13669   // "hitting something" is also true when hitting the playfield border
13670   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13671                            CE_HITTING_SOMETHING, direction);
13672 }
13673
13674 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13675 {
13676   int i, kill_x = -1, kill_y = -1;
13677
13678   int bad_element = -1;
13679   static int test_xy[4][2] =
13680   {
13681     { 0, -1 },
13682     { -1, 0 },
13683     { +1, 0 },
13684     { 0, +1 }
13685   };
13686   static int test_dir[4] =
13687   {
13688     MV_UP,
13689     MV_LEFT,
13690     MV_RIGHT,
13691     MV_DOWN
13692   };
13693
13694   for (i = 0; i < NUM_DIRECTIONS; i++)
13695   {
13696     int test_x, test_y, test_move_dir, test_element;
13697
13698     test_x = good_x + test_xy[i][0];
13699     test_y = good_y + test_xy[i][1];
13700
13701     if (!IN_LEV_FIELD(test_x, test_y))
13702       continue;
13703
13704     test_move_dir =
13705       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13706
13707     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13708
13709     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13710        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13711     */
13712     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13713         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13714     {
13715       kill_x = test_x;
13716       kill_y = test_y;
13717       bad_element = test_element;
13718
13719       break;
13720     }
13721   }
13722
13723   if (kill_x != -1 || kill_y != -1)
13724   {
13725     if (IS_PLAYER(good_x, good_y))
13726     {
13727       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13728
13729       if (player->shield_deadly_time_left > 0 &&
13730           !IS_INDESTRUCTIBLE(bad_element))
13731         Bang(kill_x, kill_y);
13732       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13733         KillPlayer(player);
13734     }
13735     else
13736       Bang(good_x, good_y);
13737   }
13738 }
13739
13740 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13741 {
13742   int i, kill_x = -1, kill_y = -1;
13743   int bad_element = Tile[bad_x][bad_y];
13744   static int test_xy[4][2] =
13745   {
13746     { 0, -1 },
13747     { -1, 0 },
13748     { +1, 0 },
13749     { 0, +1 }
13750   };
13751   static int touch_dir[4] =
13752   {
13753     MV_LEFT | MV_RIGHT,
13754     MV_UP   | MV_DOWN,
13755     MV_UP   | MV_DOWN,
13756     MV_LEFT | MV_RIGHT
13757   };
13758   static int test_dir[4] =
13759   {
13760     MV_UP,
13761     MV_LEFT,
13762     MV_RIGHT,
13763     MV_DOWN
13764   };
13765
13766   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13767     return;
13768
13769   for (i = 0; i < NUM_DIRECTIONS; i++)
13770   {
13771     int test_x, test_y, test_move_dir, test_element;
13772
13773     test_x = bad_x + test_xy[i][0];
13774     test_y = bad_y + test_xy[i][1];
13775
13776     if (!IN_LEV_FIELD(test_x, test_y))
13777       continue;
13778
13779     test_move_dir =
13780       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13781
13782     test_element = Tile[test_x][test_y];
13783
13784     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13785        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13786     */
13787     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13788         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13789     {
13790       // good thing is player or penguin that does not move away
13791       if (IS_PLAYER(test_x, test_y))
13792       {
13793         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13794
13795         if (bad_element == EL_ROBOT && player->is_moving)
13796           continue;     // robot does not kill player if he is moving
13797
13798         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13799         {
13800           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13801             continue;           // center and border element do not touch
13802         }
13803
13804         kill_x = test_x;
13805         kill_y = test_y;
13806
13807         break;
13808       }
13809       else if (test_element == EL_PENGUIN)
13810       {
13811         kill_x = test_x;
13812         kill_y = test_y;
13813
13814         break;
13815       }
13816     }
13817   }
13818
13819   if (kill_x != -1 || kill_y != -1)
13820   {
13821     if (IS_PLAYER(kill_x, kill_y))
13822     {
13823       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13824
13825       if (player->shield_deadly_time_left > 0 &&
13826           !IS_INDESTRUCTIBLE(bad_element))
13827         Bang(bad_x, bad_y);
13828       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13829         KillPlayer(player);
13830     }
13831     else
13832       Bang(kill_x, kill_y);
13833   }
13834 }
13835
13836 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13837 {
13838   int bad_element = Tile[bad_x][bad_y];
13839   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13840   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13841   int test_x = bad_x + dx, test_y = bad_y + dy;
13842   int test_move_dir, test_element;
13843   int kill_x = -1, kill_y = -1;
13844
13845   if (!IN_LEV_FIELD(test_x, test_y))
13846     return;
13847
13848   test_move_dir =
13849     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13850
13851   test_element = Tile[test_x][test_y];
13852
13853   if (test_move_dir != bad_move_dir)
13854   {
13855     // good thing can be player or penguin that does not move away
13856     if (IS_PLAYER(test_x, test_y))
13857     {
13858       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13859
13860       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13861          player as being hit when he is moving towards the bad thing, because
13862          the "get hit by" condition would be lost after the player stops) */
13863       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13864         return;         // player moves away from bad thing
13865
13866       kill_x = test_x;
13867       kill_y = test_y;
13868     }
13869     else if (test_element == EL_PENGUIN)
13870     {
13871       kill_x = test_x;
13872       kill_y = test_y;
13873     }
13874   }
13875
13876   if (kill_x != -1 || kill_y != -1)
13877   {
13878     if (IS_PLAYER(kill_x, kill_y))
13879     {
13880       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13881
13882       if (player->shield_deadly_time_left > 0 &&
13883           !IS_INDESTRUCTIBLE(bad_element))
13884         Bang(bad_x, bad_y);
13885       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13886         KillPlayer(player);
13887     }
13888     else
13889       Bang(kill_x, kill_y);
13890   }
13891 }
13892
13893 void TestIfPlayerTouchesBadThing(int x, int y)
13894 {
13895   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13896 }
13897
13898 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13899 {
13900   TestIfGoodThingHitsBadThing(x, y, move_dir);
13901 }
13902
13903 void TestIfBadThingTouchesPlayer(int x, int y)
13904 {
13905   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13906 }
13907
13908 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13909 {
13910   TestIfBadThingHitsGoodThing(x, y, move_dir);
13911 }
13912
13913 void TestIfFriendTouchesBadThing(int x, int y)
13914 {
13915   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13916 }
13917
13918 void TestIfBadThingTouchesFriend(int x, int y)
13919 {
13920   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13921 }
13922
13923 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13924 {
13925   int i, kill_x = bad_x, kill_y = bad_y;
13926   static int xy[4][2] =
13927   {
13928     { 0, -1 },
13929     { -1, 0 },
13930     { +1, 0 },
13931     { 0, +1 }
13932   };
13933
13934   for (i = 0; i < NUM_DIRECTIONS; i++)
13935   {
13936     int x, y, element;
13937
13938     x = bad_x + xy[i][0];
13939     y = bad_y + xy[i][1];
13940     if (!IN_LEV_FIELD(x, y))
13941       continue;
13942
13943     element = Tile[x][y];
13944     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13945         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13946     {
13947       kill_x = x;
13948       kill_y = y;
13949       break;
13950     }
13951   }
13952
13953   if (kill_x != bad_x || kill_y != bad_y)
13954     Bang(bad_x, bad_y);
13955 }
13956
13957 void KillPlayer(struct PlayerInfo *player)
13958 {
13959   int jx = player->jx, jy = player->jy;
13960
13961   if (!player->active)
13962     return;
13963
13964 #if 0
13965   Debug("game:playing:KillPlayer",
13966         "0: killed == %d, active == %d, reanimated == %d",
13967         player->killed, player->active, player->reanimated);
13968 #endif
13969
13970   /* the following code was introduced to prevent an infinite loop when calling
13971      -> Bang()
13972      -> CheckTriggeredElementChangeExt()
13973      -> ExecuteCustomElementAction()
13974      -> KillPlayer()
13975      -> (infinitely repeating the above sequence of function calls)
13976      which occurs when killing the player while having a CE with the setting
13977      "kill player X when explosion of <player X>"; the solution using a new
13978      field "player->killed" was chosen for backwards compatibility, although
13979      clever use of the fields "player->active" etc. would probably also work */
13980 #if 1
13981   if (player->killed)
13982     return;
13983 #endif
13984
13985   player->killed = TRUE;
13986
13987   // remove accessible field at the player's position
13988   Tile[jx][jy] = EL_EMPTY;
13989
13990   // deactivate shield (else Bang()/Explode() would not work right)
13991   player->shield_normal_time_left = 0;
13992   player->shield_deadly_time_left = 0;
13993
13994 #if 0
13995   Debug("game:playing:KillPlayer",
13996         "1: killed == %d, active == %d, reanimated == %d",
13997         player->killed, player->active, player->reanimated);
13998 #endif
13999
14000   Bang(jx, jy);
14001
14002 #if 0
14003   Debug("game:playing:KillPlayer",
14004         "2: killed == %d, active == %d, reanimated == %d",
14005         player->killed, player->active, player->reanimated);
14006 #endif
14007
14008   if (player->reanimated)       // killed player may have been reanimated
14009     player->killed = player->reanimated = FALSE;
14010   else
14011     BuryPlayer(player);
14012 }
14013
14014 static void KillPlayerUnlessEnemyProtected(int x, int y)
14015 {
14016   if (!PLAYER_ENEMY_PROTECTED(x, y))
14017     KillPlayer(PLAYERINFO(x, y));
14018 }
14019
14020 static void KillPlayerUnlessExplosionProtected(int x, int y)
14021 {
14022   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14023     KillPlayer(PLAYERINFO(x, y));
14024 }
14025
14026 void BuryPlayer(struct PlayerInfo *player)
14027 {
14028   int jx = player->jx, jy = player->jy;
14029
14030   if (!player->active)
14031     return;
14032
14033   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14034   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14035
14036   RemovePlayer(player);
14037
14038   player->buried = TRUE;
14039
14040   if (game.all_players_gone)
14041     game.GameOver = TRUE;
14042 }
14043
14044 void RemovePlayer(struct PlayerInfo *player)
14045 {
14046   int jx = player->jx, jy = player->jy;
14047   int i, found = FALSE;
14048
14049   player->present = FALSE;
14050   player->active = FALSE;
14051
14052   // required for some CE actions (even if the player is not active anymore)
14053   player->MovPos = 0;
14054
14055   if (!ExplodeField[jx][jy])
14056     StorePlayer[jx][jy] = 0;
14057
14058   if (player->is_moving)
14059     TEST_DrawLevelField(player->last_jx, player->last_jy);
14060
14061   for (i = 0; i < MAX_PLAYERS; i++)
14062     if (stored_player[i].active)
14063       found = TRUE;
14064
14065   if (!found)
14066   {
14067     game.all_players_gone = TRUE;
14068     game.GameOver = TRUE;
14069   }
14070
14071   game.exit_x = game.robot_wheel_x = jx;
14072   game.exit_y = game.robot_wheel_y = jy;
14073 }
14074
14075 void ExitPlayer(struct PlayerInfo *player)
14076 {
14077   DrawPlayer(player);   // needed here only to cleanup last field
14078   RemovePlayer(player);
14079
14080   if (game.players_still_needed > 0)
14081     game.players_still_needed--;
14082 }
14083
14084 static void SetFieldForSnapping(int x, int y, int element, int direction,
14085                                 int player_index_bit)
14086 {
14087   struct ElementInfo *ei = &element_info[element];
14088   int direction_bit = MV_DIR_TO_BIT(direction);
14089   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14090   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14091                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14092
14093   Tile[x][y] = EL_ELEMENT_SNAPPING;
14094   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14095   MovDir[x][y] = direction;
14096   Store[x][y] = element;
14097   Store2[x][y] = player_index_bit;
14098
14099   ResetGfxAnimation(x, y);
14100
14101   GfxElement[x][y] = element;
14102   GfxAction[x][y] = action;
14103   GfxDir[x][y] = direction;
14104   GfxFrame[x][y] = -1;
14105 }
14106
14107 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14108                                    int player_index_bit)
14109 {
14110   TestIfElementTouchesCustomElement(x, y);      // for empty space
14111
14112   if (level.finish_dig_collect)
14113   {
14114     int dig_side = MV_DIR_OPPOSITE(direction);
14115     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14116                         CE_PLAYER_COLLECTS_X);
14117
14118     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14119                                         player_index_bit, dig_side);
14120     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14121                                         player_index_bit, dig_side);
14122   }
14123 }
14124
14125 /*
14126   =============================================================================
14127   checkDiagonalPushing()
14128   -----------------------------------------------------------------------------
14129   check if diagonal input device direction results in pushing of object
14130   (by checking if the alternative direction is walkable, diggable, ...)
14131   =============================================================================
14132 */
14133
14134 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14135                                     int x, int y, int real_dx, int real_dy)
14136 {
14137   int jx, jy, dx, dy, xx, yy;
14138
14139   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14140     return TRUE;
14141
14142   // diagonal direction: check alternative direction
14143   jx = player->jx;
14144   jy = player->jy;
14145   dx = x - jx;
14146   dy = y - jy;
14147   xx = jx + (dx == 0 ? real_dx : 0);
14148   yy = jy + (dy == 0 ? real_dy : 0);
14149
14150   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14151 }
14152
14153 /*
14154   =============================================================================
14155   DigField()
14156   -----------------------------------------------------------------------------
14157   x, y:                 field next to player (non-diagonal) to try to dig to
14158   real_dx, real_dy:     direction as read from input device (can be diagonal)
14159   =============================================================================
14160 */
14161
14162 static int DigField(struct PlayerInfo *player,
14163                     int oldx, int oldy, int x, int y,
14164                     int real_dx, int real_dy, int mode)
14165 {
14166   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14167   boolean player_was_pushing = player->is_pushing;
14168   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14169   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14170   int jx = oldx, jy = oldy;
14171   int dx = x - jx, dy = y - jy;
14172   int nextx = x + dx, nexty = y + dy;
14173   int move_direction = (dx == -1 ? MV_LEFT  :
14174                         dx == +1 ? MV_RIGHT :
14175                         dy == -1 ? MV_UP    :
14176                         dy == +1 ? MV_DOWN  : MV_NONE);
14177   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14178   int dig_side = MV_DIR_OPPOSITE(move_direction);
14179   int old_element = Tile[jx][jy];
14180   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14181   int collect_count;
14182
14183   if (is_player)                // function can also be called by EL_PENGUIN
14184   {
14185     if (player->MovPos == 0)
14186     {
14187       player->is_digging = FALSE;
14188       player->is_collecting = FALSE;
14189     }
14190
14191     if (player->MovPos == 0)    // last pushing move finished
14192       player->is_pushing = FALSE;
14193
14194     if (mode == DF_NO_PUSH)     // player just stopped pushing
14195     {
14196       player->is_switching = FALSE;
14197       player->push_delay = -1;
14198
14199       return MP_NO_ACTION;
14200     }
14201   }
14202
14203   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14204     old_element = Back[jx][jy];
14205
14206   // in case of element dropped at player position, check background
14207   else if (Back[jx][jy] != EL_EMPTY &&
14208            game.engine_version >= VERSION_IDENT(2,2,0,0))
14209     old_element = Back[jx][jy];
14210
14211   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14212     return MP_NO_ACTION;        // field has no opening in this direction
14213
14214   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14215     return MP_NO_ACTION;        // field has no opening in this direction
14216
14217   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14218   {
14219     SplashAcid(x, y);
14220
14221     Tile[jx][jy] = player->artwork_element;
14222     InitMovingField(jx, jy, MV_DOWN);
14223     Store[jx][jy] = EL_ACID;
14224     ContinueMoving(jx, jy);
14225     BuryPlayer(player);
14226
14227     return MP_DONT_RUN_INTO;
14228   }
14229
14230   if (player_can_move && DONT_RUN_INTO(element))
14231   {
14232     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14233
14234     return MP_DONT_RUN_INTO;
14235   }
14236
14237   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14238     return MP_NO_ACTION;
14239
14240   collect_count = element_info[element].collect_count_initial;
14241
14242   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14243     return MP_NO_ACTION;
14244
14245   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14246     player_can_move = player_can_move_or_snap;
14247
14248   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14249       game.engine_version >= VERSION_IDENT(2,2,0,0))
14250   {
14251     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14252                                player->index_bit, dig_side);
14253     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14254                                         player->index_bit, dig_side);
14255
14256     if (element == EL_DC_LANDMINE)
14257       Bang(x, y);
14258
14259     if (Tile[x][y] != element)          // field changed by snapping
14260       return MP_ACTION;
14261
14262     return MP_NO_ACTION;
14263   }
14264
14265   if (player->gravity && is_player && !player->is_auto_moving &&
14266       canFallDown(player) && move_direction != MV_DOWN &&
14267       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14268     return MP_NO_ACTION;        // player cannot walk here due to gravity
14269
14270   if (player_can_move &&
14271       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14272   {
14273     int sound_element = SND_ELEMENT(element);
14274     int sound_action = ACTION_WALKING;
14275
14276     if (IS_RND_GATE(element))
14277     {
14278       if (!player->key[RND_GATE_NR(element)])
14279         return MP_NO_ACTION;
14280     }
14281     else if (IS_RND_GATE_GRAY(element))
14282     {
14283       if (!player->key[RND_GATE_GRAY_NR(element)])
14284         return MP_NO_ACTION;
14285     }
14286     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14287     {
14288       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14289         return MP_NO_ACTION;
14290     }
14291     else if (element == EL_EXIT_OPEN ||
14292              element == EL_EM_EXIT_OPEN ||
14293              element == EL_EM_EXIT_OPENING ||
14294              element == EL_STEEL_EXIT_OPEN ||
14295              element == EL_EM_STEEL_EXIT_OPEN ||
14296              element == EL_EM_STEEL_EXIT_OPENING ||
14297              element == EL_SP_EXIT_OPEN ||
14298              element == EL_SP_EXIT_OPENING)
14299     {
14300       sound_action = ACTION_PASSING;    // player is passing exit
14301     }
14302     else if (element == EL_EMPTY)
14303     {
14304       sound_action = ACTION_MOVING;             // nothing to walk on
14305     }
14306
14307     // play sound from background or player, whatever is available
14308     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14309       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14310     else
14311       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14312   }
14313   else if (player_can_move &&
14314            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14315   {
14316     if (!ACCESS_FROM(element, opposite_direction))
14317       return MP_NO_ACTION;      // field not accessible from this direction
14318
14319     if (CAN_MOVE(element))      // only fixed elements can be passed!
14320       return MP_NO_ACTION;
14321
14322     if (IS_EM_GATE(element))
14323     {
14324       if (!player->key[EM_GATE_NR(element)])
14325         return MP_NO_ACTION;
14326     }
14327     else if (IS_EM_GATE_GRAY(element))
14328     {
14329       if (!player->key[EM_GATE_GRAY_NR(element)])
14330         return MP_NO_ACTION;
14331     }
14332     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14333     {
14334       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14335         return MP_NO_ACTION;
14336     }
14337     else if (IS_EMC_GATE(element))
14338     {
14339       if (!player->key[EMC_GATE_NR(element)])
14340         return MP_NO_ACTION;
14341     }
14342     else if (IS_EMC_GATE_GRAY(element))
14343     {
14344       if (!player->key[EMC_GATE_GRAY_NR(element)])
14345         return MP_NO_ACTION;
14346     }
14347     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14348     {
14349       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14350         return MP_NO_ACTION;
14351     }
14352     else if (element == EL_DC_GATE_WHITE ||
14353              element == EL_DC_GATE_WHITE_GRAY ||
14354              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14355     {
14356       if (player->num_white_keys == 0)
14357         return MP_NO_ACTION;
14358
14359       player->num_white_keys--;
14360     }
14361     else if (IS_SP_PORT(element))
14362     {
14363       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14364           element == EL_SP_GRAVITY_PORT_RIGHT ||
14365           element == EL_SP_GRAVITY_PORT_UP ||
14366           element == EL_SP_GRAVITY_PORT_DOWN)
14367         player->gravity = !player->gravity;
14368       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14369                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14370                element == EL_SP_GRAVITY_ON_PORT_UP ||
14371                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14372         player->gravity = TRUE;
14373       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14374                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14375                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14376                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14377         player->gravity = FALSE;
14378     }
14379
14380     // automatically move to the next field with double speed
14381     player->programmed_action = move_direction;
14382
14383     if (player->move_delay_reset_counter == 0)
14384     {
14385       player->move_delay_reset_counter = 2;     // two double speed steps
14386
14387       DOUBLE_PLAYER_SPEED(player);
14388     }
14389
14390     PlayLevelSoundAction(x, y, ACTION_PASSING);
14391   }
14392   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14393   {
14394     RemoveField(x, y);
14395
14396     if (mode != DF_SNAP)
14397     {
14398       GfxElement[x][y] = GFX_ELEMENT(element);
14399       player->is_digging = TRUE;
14400     }
14401
14402     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14403
14404     // use old behaviour for old levels (digging)
14405     if (!level.finish_dig_collect)
14406     {
14407       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14408                                           player->index_bit, dig_side);
14409
14410       // if digging triggered player relocation, finish digging tile
14411       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14412         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14413     }
14414
14415     if (mode == DF_SNAP)
14416     {
14417       if (level.block_snap_field)
14418         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14419       else
14420         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14421
14422       // use old behaviour for old levels (snapping)
14423       if (!level.finish_dig_collect)
14424         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14425                                             player->index_bit, dig_side);
14426     }
14427   }
14428   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14429   {
14430     RemoveField(x, y);
14431
14432     if (is_player && mode != DF_SNAP)
14433     {
14434       GfxElement[x][y] = element;
14435       player->is_collecting = TRUE;
14436     }
14437
14438     if (element == EL_SPEED_PILL)
14439     {
14440       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14441     }
14442     else if (element == EL_EXTRA_TIME && level.time > 0)
14443     {
14444       TimeLeft += level.extra_time;
14445
14446       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14447
14448       DisplayGameControlValues();
14449     }
14450     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14451     {
14452       player->shield_normal_time_left += level.shield_normal_time;
14453       if (element == EL_SHIELD_DEADLY)
14454         player->shield_deadly_time_left += level.shield_deadly_time;
14455     }
14456     else if (element == EL_DYNAMITE ||
14457              element == EL_EM_DYNAMITE ||
14458              element == EL_SP_DISK_RED)
14459     {
14460       if (player->inventory_size < MAX_INVENTORY_SIZE)
14461         player->inventory_element[player->inventory_size++] = element;
14462
14463       DrawGameDoorValues();
14464     }
14465     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14466     {
14467       player->dynabomb_count++;
14468       player->dynabombs_left++;
14469     }
14470     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14471     {
14472       player->dynabomb_size++;
14473     }
14474     else if (element == EL_DYNABOMB_INCREASE_POWER)
14475     {
14476       player->dynabomb_xl = TRUE;
14477     }
14478     else if (IS_KEY(element))
14479     {
14480       player->key[KEY_NR(element)] = TRUE;
14481
14482       DrawGameDoorValues();
14483     }
14484     else if (element == EL_DC_KEY_WHITE)
14485     {
14486       player->num_white_keys++;
14487
14488       // display white keys?
14489       // DrawGameDoorValues();
14490     }
14491     else if (IS_ENVELOPE(element))
14492     {
14493       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14494
14495       if (!wait_for_snapping)
14496         player->show_envelope = element;
14497     }
14498     else if (element == EL_EMC_LENSES)
14499     {
14500       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14501
14502       RedrawAllInvisibleElementsForLenses();
14503     }
14504     else if (element == EL_EMC_MAGNIFIER)
14505     {
14506       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14507
14508       RedrawAllInvisibleElementsForMagnifier();
14509     }
14510     else if (IS_DROPPABLE(element) ||
14511              IS_THROWABLE(element))     // can be collected and dropped
14512     {
14513       int i;
14514
14515       if (collect_count == 0)
14516         player->inventory_infinite_element = element;
14517       else
14518         for (i = 0; i < collect_count; i++)
14519           if (player->inventory_size < MAX_INVENTORY_SIZE)
14520             player->inventory_element[player->inventory_size++] = element;
14521
14522       DrawGameDoorValues();
14523     }
14524     else if (collect_count > 0)
14525     {
14526       game.gems_still_needed -= collect_count;
14527       if (game.gems_still_needed < 0)
14528         game.gems_still_needed = 0;
14529
14530       game.snapshot.collected_item = TRUE;
14531
14532       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14533
14534       DisplayGameControlValues();
14535     }
14536
14537     RaiseScoreElement(element);
14538     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14539
14540     // use old behaviour for old levels (collecting)
14541     if (!level.finish_dig_collect && is_player)
14542     {
14543       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14544                                           player->index_bit, dig_side);
14545
14546       // if collecting triggered player relocation, finish collecting tile
14547       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14548         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14549     }
14550
14551     if (mode == DF_SNAP)
14552     {
14553       if (level.block_snap_field)
14554         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14555       else
14556         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14557
14558       // use old behaviour for old levels (snapping)
14559       if (!level.finish_dig_collect)
14560         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14561                                             player->index_bit, dig_side);
14562     }
14563   }
14564   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14565   {
14566     if (mode == DF_SNAP && element != EL_BD_ROCK)
14567       return MP_NO_ACTION;
14568
14569     if (CAN_FALL(element) && dy)
14570       return MP_NO_ACTION;
14571
14572     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14573         !(element == EL_SPRING && level.use_spring_bug))
14574       return MP_NO_ACTION;
14575
14576     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14577         ((move_direction & MV_VERTICAL &&
14578           ((element_info[element].move_pattern & MV_LEFT &&
14579             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14580            (element_info[element].move_pattern & MV_RIGHT &&
14581             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14582          (move_direction & MV_HORIZONTAL &&
14583           ((element_info[element].move_pattern & MV_UP &&
14584             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14585            (element_info[element].move_pattern & MV_DOWN &&
14586             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14587       return MP_NO_ACTION;
14588
14589     // do not push elements already moving away faster than player
14590     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14591         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14592       return MP_NO_ACTION;
14593
14594     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14595     {
14596       if (player->push_delay_value == -1 || !player_was_pushing)
14597         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14598     }
14599     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14600     {
14601       if (player->push_delay_value == -1)
14602         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14603     }
14604     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14605     {
14606       if (!player->is_pushing)
14607         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14608     }
14609
14610     player->is_pushing = TRUE;
14611     player->is_active = TRUE;
14612
14613     if (!(IN_LEV_FIELD(nextx, nexty) &&
14614           (IS_FREE(nextx, nexty) ||
14615            (IS_SB_ELEMENT(element) &&
14616             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14617            (IS_CUSTOM_ELEMENT(element) &&
14618             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14619       return MP_NO_ACTION;
14620
14621     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14622       return MP_NO_ACTION;
14623
14624     if (player->push_delay == -1)       // new pushing; restart delay
14625       player->push_delay = 0;
14626
14627     if (player->push_delay < player->push_delay_value &&
14628         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14629         element != EL_SPRING && element != EL_BALLOON)
14630     {
14631       // make sure that there is no move delay before next try to push
14632       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14633         player->move_delay = 0;
14634
14635       return MP_NO_ACTION;
14636     }
14637
14638     if (IS_CUSTOM_ELEMENT(element) &&
14639         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14640     {
14641       if (!DigFieldByCE(nextx, nexty, element))
14642         return MP_NO_ACTION;
14643     }
14644
14645     if (IS_SB_ELEMENT(element))
14646     {
14647       boolean sokoban_task_solved = FALSE;
14648
14649       if (element == EL_SOKOBAN_FIELD_FULL)
14650       {
14651         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14652
14653         IncrementSokobanFieldsNeeded();
14654         IncrementSokobanObjectsNeeded();
14655       }
14656
14657       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14658       {
14659         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14660
14661         DecrementSokobanFieldsNeeded();
14662         DecrementSokobanObjectsNeeded();
14663
14664         // sokoban object was pushed from empty field to sokoban field
14665         if (Back[x][y] == EL_EMPTY)
14666           sokoban_task_solved = TRUE;
14667       }
14668
14669       Tile[x][y] = EL_SOKOBAN_OBJECT;
14670
14671       if (Back[x][y] == Back[nextx][nexty])
14672         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14673       else if (Back[x][y] != 0)
14674         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14675                                     ACTION_EMPTYING);
14676       else
14677         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14678                                     ACTION_FILLING);
14679
14680       if (sokoban_task_solved &&
14681           game.sokoban_fields_still_needed == 0 &&
14682           game.sokoban_objects_still_needed == 0 &&
14683           level.auto_exit_sokoban)
14684       {
14685         game.players_still_needed = 0;
14686
14687         LevelSolved();
14688
14689         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14690       }
14691     }
14692     else
14693       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14694
14695     InitMovingField(x, y, move_direction);
14696     GfxAction[x][y] = ACTION_PUSHING;
14697
14698     if (mode == DF_SNAP)
14699       ContinueMoving(x, y);
14700     else
14701       MovPos[x][y] = (dx != 0 ? dx : dy);
14702
14703     Pushed[x][y] = TRUE;
14704     Pushed[nextx][nexty] = TRUE;
14705
14706     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14707       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14708     else
14709       player->push_delay_value = -1;    // get new value later
14710
14711     // check for element change _after_ element has been pushed
14712     if (game.use_change_when_pushing_bug)
14713     {
14714       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14715                                  player->index_bit, dig_side);
14716       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14717                                           player->index_bit, dig_side);
14718     }
14719   }
14720   else if (IS_SWITCHABLE(element))
14721   {
14722     if (PLAYER_SWITCHING(player, x, y))
14723     {
14724       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14725                                           player->index_bit, dig_side);
14726
14727       return MP_ACTION;
14728     }
14729
14730     player->is_switching = TRUE;
14731     player->switch_x = x;
14732     player->switch_y = y;
14733
14734     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14735
14736     if (element == EL_ROBOT_WHEEL)
14737     {
14738       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14739
14740       game.robot_wheel_x = x;
14741       game.robot_wheel_y = y;
14742       game.robot_wheel_active = TRUE;
14743
14744       TEST_DrawLevelField(x, y);
14745     }
14746     else if (element == EL_SP_TERMINAL)
14747     {
14748       int xx, yy;
14749
14750       SCAN_PLAYFIELD(xx, yy)
14751       {
14752         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14753         {
14754           Bang(xx, yy);
14755         }
14756         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14757         {
14758           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14759
14760           ResetGfxAnimation(xx, yy);
14761           TEST_DrawLevelField(xx, yy);
14762         }
14763       }
14764     }
14765     else if (IS_BELT_SWITCH(element))
14766     {
14767       ToggleBeltSwitch(x, y);
14768     }
14769     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14770              element == EL_SWITCHGATE_SWITCH_DOWN ||
14771              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14772              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14773     {
14774       ToggleSwitchgateSwitch(x, y);
14775     }
14776     else if (element == EL_LIGHT_SWITCH ||
14777              element == EL_LIGHT_SWITCH_ACTIVE)
14778     {
14779       ToggleLightSwitch(x, y);
14780     }
14781     else if (element == EL_TIMEGATE_SWITCH ||
14782              element == EL_DC_TIMEGATE_SWITCH)
14783     {
14784       ActivateTimegateSwitch(x, y);
14785     }
14786     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14787              element == EL_BALLOON_SWITCH_RIGHT ||
14788              element == EL_BALLOON_SWITCH_UP    ||
14789              element == EL_BALLOON_SWITCH_DOWN  ||
14790              element == EL_BALLOON_SWITCH_NONE  ||
14791              element == EL_BALLOON_SWITCH_ANY)
14792     {
14793       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14794                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14795                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14796                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14797                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14798                              move_direction);
14799     }
14800     else if (element == EL_LAMP)
14801     {
14802       Tile[x][y] = EL_LAMP_ACTIVE;
14803       game.lights_still_needed--;
14804
14805       ResetGfxAnimation(x, y);
14806       TEST_DrawLevelField(x, y);
14807     }
14808     else if (element == EL_TIME_ORB_FULL)
14809     {
14810       Tile[x][y] = EL_TIME_ORB_EMPTY;
14811
14812       if (level.time > 0 || level.use_time_orb_bug)
14813       {
14814         TimeLeft += level.time_orb_time;
14815         game.no_time_limit = FALSE;
14816
14817         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14818
14819         DisplayGameControlValues();
14820       }
14821
14822       ResetGfxAnimation(x, y);
14823       TEST_DrawLevelField(x, y);
14824     }
14825     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14826              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14827     {
14828       int xx, yy;
14829
14830       game.ball_active = !game.ball_active;
14831
14832       SCAN_PLAYFIELD(xx, yy)
14833       {
14834         int e = Tile[xx][yy];
14835
14836         if (game.ball_active)
14837         {
14838           if (e == EL_EMC_MAGIC_BALL)
14839             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14840           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14841             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14842         }
14843         else
14844         {
14845           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14846             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14847           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14848             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14849         }
14850       }
14851     }
14852
14853     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14854                                         player->index_bit, dig_side);
14855
14856     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14857                                         player->index_bit, dig_side);
14858
14859     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14860                                         player->index_bit, dig_side);
14861
14862     return MP_ACTION;
14863   }
14864   else
14865   {
14866     if (!PLAYER_SWITCHING(player, x, y))
14867     {
14868       player->is_switching = TRUE;
14869       player->switch_x = x;
14870       player->switch_y = y;
14871
14872       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14873                                  player->index_bit, dig_side);
14874       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14875                                           player->index_bit, dig_side);
14876
14877       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14878                                  player->index_bit, dig_side);
14879       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14880                                           player->index_bit, dig_side);
14881     }
14882
14883     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14884                                player->index_bit, dig_side);
14885     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14886                                         player->index_bit, dig_side);
14887
14888     return MP_NO_ACTION;
14889   }
14890
14891   player->push_delay = -1;
14892
14893   if (is_player)                // function can also be called by EL_PENGUIN
14894   {
14895     if (Tile[x][y] != element)          // really digged/collected something
14896     {
14897       player->is_collecting = !player->is_digging;
14898       player->is_active = TRUE;
14899
14900       player->last_removed_element = element;
14901     }
14902   }
14903
14904   return MP_MOVING;
14905 }
14906
14907 static boolean DigFieldByCE(int x, int y, int digging_element)
14908 {
14909   int element = Tile[x][y];
14910
14911   if (!IS_FREE(x, y))
14912   {
14913     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14914                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14915                   ACTION_BREAKING);
14916
14917     // no element can dig solid indestructible elements
14918     if (IS_INDESTRUCTIBLE(element) &&
14919         !IS_DIGGABLE(element) &&
14920         !IS_COLLECTIBLE(element))
14921       return FALSE;
14922
14923     if (AmoebaNr[x][y] &&
14924         (element == EL_AMOEBA_FULL ||
14925          element == EL_BD_AMOEBA ||
14926          element == EL_AMOEBA_GROWING))
14927     {
14928       AmoebaCnt[AmoebaNr[x][y]]--;
14929       AmoebaCnt2[AmoebaNr[x][y]]--;
14930     }
14931
14932     if (IS_MOVING(x, y))
14933       RemoveMovingField(x, y);
14934     else
14935     {
14936       RemoveField(x, y);
14937       TEST_DrawLevelField(x, y);
14938     }
14939
14940     // if digged element was about to explode, prevent the explosion
14941     ExplodeField[x][y] = EX_TYPE_NONE;
14942
14943     PlayLevelSoundAction(x, y, action);
14944   }
14945
14946   Store[x][y] = EL_EMPTY;
14947
14948   // this makes it possible to leave the removed element again
14949   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14950     Store[x][y] = element;
14951
14952   return TRUE;
14953 }
14954
14955 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14956 {
14957   int jx = player->jx, jy = player->jy;
14958   int x = jx + dx, y = jy + dy;
14959   int snap_direction = (dx == -1 ? MV_LEFT  :
14960                         dx == +1 ? MV_RIGHT :
14961                         dy == -1 ? MV_UP    :
14962                         dy == +1 ? MV_DOWN  : MV_NONE);
14963   boolean can_continue_snapping = (level.continuous_snapping &&
14964                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14965
14966   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14967     return FALSE;
14968
14969   if (!player->active || !IN_LEV_FIELD(x, y))
14970     return FALSE;
14971
14972   if (dx && dy)
14973     return FALSE;
14974
14975   if (!dx && !dy)
14976   {
14977     if (player->MovPos == 0)
14978       player->is_pushing = FALSE;
14979
14980     player->is_snapping = FALSE;
14981
14982     if (player->MovPos == 0)
14983     {
14984       player->is_moving = FALSE;
14985       player->is_digging = FALSE;
14986       player->is_collecting = FALSE;
14987     }
14988
14989     return FALSE;
14990   }
14991
14992   // prevent snapping with already pressed snap key when not allowed
14993   if (player->is_snapping && !can_continue_snapping)
14994     return FALSE;
14995
14996   player->MovDir = snap_direction;
14997
14998   if (player->MovPos == 0)
14999   {
15000     player->is_moving = FALSE;
15001     player->is_digging = FALSE;
15002     player->is_collecting = FALSE;
15003   }
15004
15005   player->is_dropping = FALSE;
15006   player->is_dropping_pressed = FALSE;
15007   player->drop_pressed_delay = 0;
15008
15009   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15010     return FALSE;
15011
15012   player->is_snapping = TRUE;
15013   player->is_active = TRUE;
15014
15015   if (player->MovPos == 0)
15016   {
15017     player->is_moving = FALSE;
15018     player->is_digging = FALSE;
15019     player->is_collecting = FALSE;
15020   }
15021
15022   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15023     TEST_DrawLevelField(player->last_jx, player->last_jy);
15024
15025   TEST_DrawLevelField(x, y);
15026
15027   return TRUE;
15028 }
15029
15030 static boolean DropElement(struct PlayerInfo *player)
15031 {
15032   int old_element, new_element;
15033   int dropx = player->jx, dropy = player->jy;
15034   int drop_direction = player->MovDir;
15035   int drop_side = drop_direction;
15036   int drop_element = get_next_dropped_element(player);
15037
15038   /* do not drop an element on top of another element; when holding drop key
15039      pressed without moving, dropped element must move away before the next
15040      element can be dropped (this is especially important if the next element
15041      is dynamite, which can be placed on background for historical reasons) */
15042   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15043     return MP_ACTION;
15044
15045   if (IS_THROWABLE(drop_element))
15046   {
15047     dropx += GET_DX_FROM_DIR(drop_direction);
15048     dropy += GET_DY_FROM_DIR(drop_direction);
15049
15050     if (!IN_LEV_FIELD(dropx, dropy))
15051       return FALSE;
15052   }
15053
15054   old_element = Tile[dropx][dropy];     // old element at dropping position
15055   new_element = drop_element;           // default: no change when dropping
15056
15057   // check if player is active, not moving and ready to drop
15058   if (!player->active || player->MovPos || player->drop_delay > 0)
15059     return FALSE;
15060
15061   // check if player has anything that can be dropped
15062   if (new_element == EL_UNDEFINED)
15063     return FALSE;
15064
15065   // only set if player has anything that can be dropped
15066   player->is_dropping_pressed = TRUE;
15067
15068   // check if drop key was pressed long enough for EM style dynamite
15069   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15070     return FALSE;
15071
15072   // check if anything can be dropped at the current position
15073   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15074     return FALSE;
15075
15076   // collected custom elements can only be dropped on empty fields
15077   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15078     return FALSE;
15079
15080   if (old_element != EL_EMPTY)
15081     Back[dropx][dropy] = old_element;   // store old element on this field
15082
15083   ResetGfxAnimation(dropx, dropy);
15084   ResetRandomAnimationValue(dropx, dropy);
15085
15086   if (player->inventory_size > 0 ||
15087       player->inventory_infinite_element != EL_UNDEFINED)
15088   {
15089     if (player->inventory_size > 0)
15090     {
15091       player->inventory_size--;
15092
15093       DrawGameDoorValues();
15094
15095       if (new_element == EL_DYNAMITE)
15096         new_element = EL_DYNAMITE_ACTIVE;
15097       else if (new_element == EL_EM_DYNAMITE)
15098         new_element = EL_EM_DYNAMITE_ACTIVE;
15099       else if (new_element == EL_SP_DISK_RED)
15100         new_element = EL_SP_DISK_RED_ACTIVE;
15101     }
15102
15103     Tile[dropx][dropy] = new_element;
15104
15105     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15106       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15107                           el2img(Tile[dropx][dropy]), 0);
15108
15109     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15110
15111     // needed if previous element just changed to "empty" in the last frame
15112     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15113
15114     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15115                                player->index_bit, drop_side);
15116     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15117                                         CE_PLAYER_DROPS_X,
15118                                         player->index_bit, drop_side);
15119
15120     TestIfElementTouchesCustomElement(dropx, dropy);
15121   }
15122   else          // player is dropping a dyna bomb
15123   {
15124     player->dynabombs_left--;
15125
15126     Tile[dropx][dropy] = new_element;
15127
15128     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15129       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15130                           el2img(Tile[dropx][dropy]), 0);
15131
15132     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15133   }
15134
15135   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15136     InitField_WithBug1(dropx, dropy, FALSE);
15137
15138   new_element = Tile[dropx][dropy];     // element might have changed
15139
15140   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15141       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15142   {
15143     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15144       MovDir[dropx][dropy] = drop_direction;
15145
15146     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15147
15148     // do not cause impact style collision by dropping elements that can fall
15149     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15150   }
15151
15152   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15153   player->is_dropping = TRUE;
15154
15155   player->drop_pressed_delay = 0;
15156   player->is_dropping_pressed = FALSE;
15157
15158   player->drop_x = dropx;
15159   player->drop_y = dropy;
15160
15161   return TRUE;
15162 }
15163
15164 // ----------------------------------------------------------------------------
15165 // game sound playing functions
15166 // ----------------------------------------------------------------------------
15167
15168 static int *loop_sound_frame = NULL;
15169 static int *loop_sound_volume = NULL;
15170
15171 void InitPlayLevelSound(void)
15172 {
15173   int num_sounds = getSoundListSize();
15174
15175   checked_free(loop_sound_frame);
15176   checked_free(loop_sound_volume);
15177
15178   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15179   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15180 }
15181
15182 static void PlayLevelSound(int x, int y, int nr)
15183 {
15184   int sx = SCREENX(x), sy = SCREENY(y);
15185   int volume, stereo_position;
15186   int max_distance = 8;
15187   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15188
15189   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15190       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15191     return;
15192
15193   if (!IN_LEV_FIELD(x, y) ||
15194       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15195       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15196     return;
15197
15198   volume = SOUND_MAX_VOLUME;
15199
15200   if (!IN_SCR_FIELD(sx, sy))
15201   {
15202     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15203     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15204
15205     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15206   }
15207
15208   stereo_position = (SOUND_MAX_LEFT +
15209                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15210                      (SCR_FIELDX + 2 * max_distance));
15211
15212   if (IS_LOOP_SOUND(nr))
15213   {
15214     /* This assures that quieter loop sounds do not overwrite louder ones,
15215        while restarting sound volume comparison with each new game frame. */
15216
15217     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15218       return;
15219
15220     loop_sound_volume[nr] = volume;
15221     loop_sound_frame[nr] = FrameCounter;
15222   }
15223
15224   PlaySoundExt(nr, volume, stereo_position, type);
15225 }
15226
15227 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15228 {
15229   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15230                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15231                  y < LEVELY(BY1) ? LEVELY(BY1) :
15232                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15233                  sound_action);
15234 }
15235
15236 static void PlayLevelSoundAction(int x, int y, int action)
15237 {
15238   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15239 }
15240
15241 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15242 {
15243   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15244
15245   if (sound_effect != SND_UNDEFINED)
15246     PlayLevelSound(x, y, sound_effect);
15247 }
15248
15249 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15250                                               int action)
15251 {
15252   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15253
15254   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15255     PlayLevelSound(x, y, sound_effect);
15256 }
15257
15258 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15259 {
15260   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15261
15262   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15263     PlayLevelSound(x, y, sound_effect);
15264 }
15265
15266 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15267 {
15268   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15269
15270   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15271     StopSound(sound_effect);
15272 }
15273
15274 static int getLevelMusicNr(void)
15275 {
15276   if (levelset.music[level_nr] != MUS_UNDEFINED)
15277     return levelset.music[level_nr];            // from config file
15278   else
15279     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15280 }
15281
15282 static void FadeLevelSounds(void)
15283 {
15284   FadeSounds();
15285 }
15286
15287 static void FadeLevelMusic(void)
15288 {
15289   int music_nr = getLevelMusicNr();
15290   char *curr_music = getCurrentlyPlayingMusicFilename();
15291   char *next_music = getMusicInfoEntryFilename(music_nr);
15292
15293   if (!strEqual(curr_music, next_music))
15294     FadeMusic();
15295 }
15296
15297 void FadeLevelSoundsAndMusic(void)
15298 {
15299   FadeLevelSounds();
15300   FadeLevelMusic();
15301 }
15302
15303 static void PlayLevelMusic(void)
15304 {
15305   int music_nr = getLevelMusicNr();
15306   char *curr_music = getCurrentlyPlayingMusicFilename();
15307   char *next_music = getMusicInfoEntryFilename(music_nr);
15308
15309   if (!strEqual(curr_music, next_music))
15310     PlayMusicLoop(music_nr);
15311 }
15312
15313 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15314 {
15315   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15316   int offset = 0;
15317   int x = xx - offset;
15318   int y = yy - offset;
15319
15320   switch (sample)
15321   {
15322     case SOUND_blank:
15323       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15324       break;
15325
15326     case SOUND_roll:
15327       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15328       break;
15329
15330     case SOUND_stone:
15331       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15332       break;
15333
15334     case SOUND_nut:
15335       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15336       break;
15337
15338     case SOUND_crack:
15339       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15340       break;
15341
15342     case SOUND_bug:
15343       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15344       break;
15345
15346     case SOUND_tank:
15347       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15348       break;
15349
15350     case SOUND_android_clone:
15351       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15352       break;
15353
15354     case SOUND_android_move:
15355       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15356       break;
15357
15358     case SOUND_spring:
15359       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15360       break;
15361
15362     case SOUND_slurp:
15363       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15364       break;
15365
15366     case SOUND_eater:
15367       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15368       break;
15369
15370     case SOUND_eater_eat:
15371       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15372       break;
15373
15374     case SOUND_alien:
15375       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15376       break;
15377
15378     case SOUND_collect:
15379       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15380       break;
15381
15382     case SOUND_diamond:
15383       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15384       break;
15385
15386     case SOUND_squash:
15387       // !!! CHECK THIS !!!
15388 #if 1
15389       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15390 #else
15391       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15392 #endif
15393       break;
15394
15395     case SOUND_wonderfall:
15396       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15397       break;
15398
15399     case SOUND_drip:
15400       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15401       break;
15402
15403     case SOUND_push:
15404       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15405       break;
15406
15407     case SOUND_dirt:
15408       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15409       break;
15410
15411     case SOUND_acid:
15412       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15413       break;
15414
15415     case SOUND_ball:
15416       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15417       break;
15418
15419     case SOUND_slide:
15420       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15421       break;
15422
15423     case SOUND_wonder:
15424       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15425       break;
15426
15427     case SOUND_door:
15428       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15429       break;
15430
15431     case SOUND_exit_open:
15432       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15433       break;
15434
15435     case SOUND_exit_leave:
15436       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15437       break;
15438
15439     case SOUND_dynamite:
15440       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15441       break;
15442
15443     case SOUND_tick:
15444       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15445       break;
15446
15447     case SOUND_press:
15448       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15449       break;
15450
15451     case SOUND_wheel:
15452       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15453       break;
15454
15455     case SOUND_boom:
15456       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15457       break;
15458
15459     case SOUND_die:
15460       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15461       break;
15462
15463     case SOUND_time:
15464       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15465       break;
15466
15467     default:
15468       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15469       break;
15470   }
15471 }
15472
15473 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15474 {
15475   int element = map_element_SP_to_RND(element_sp);
15476   int action = map_action_SP_to_RND(action_sp);
15477   int offset = (setup.sp_show_border_elements ? 0 : 1);
15478   int x = xx - offset;
15479   int y = yy - offset;
15480
15481   PlayLevelSoundElementAction(x, y, element, action);
15482 }
15483
15484 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15485 {
15486   int element = map_element_MM_to_RND(element_mm);
15487   int action = map_action_MM_to_RND(action_mm);
15488   int offset = 0;
15489   int x = xx - offset;
15490   int y = yy - offset;
15491
15492   if (!IS_MM_ELEMENT(element))
15493     element = EL_MM_DEFAULT;
15494
15495   PlayLevelSoundElementAction(x, y, element, action);
15496 }
15497
15498 void PlaySound_MM(int sound_mm)
15499 {
15500   int sound = map_sound_MM_to_RND(sound_mm);
15501
15502   if (sound == SND_UNDEFINED)
15503     return;
15504
15505   PlaySound(sound);
15506 }
15507
15508 void PlaySoundLoop_MM(int sound_mm)
15509 {
15510   int sound = map_sound_MM_to_RND(sound_mm);
15511
15512   if (sound == SND_UNDEFINED)
15513     return;
15514
15515   PlaySoundLoop(sound);
15516 }
15517
15518 void StopSound_MM(int sound_mm)
15519 {
15520   int sound = map_sound_MM_to_RND(sound_mm);
15521
15522   if (sound == SND_UNDEFINED)
15523     return;
15524
15525   StopSound(sound);
15526 }
15527
15528 void RaiseScore(int value)
15529 {
15530   game.score += value;
15531
15532   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15533
15534   DisplayGameControlValues();
15535 }
15536
15537 void RaiseScoreElement(int element)
15538 {
15539   switch (element)
15540   {
15541     case EL_EMERALD:
15542     case EL_BD_DIAMOND:
15543     case EL_EMERALD_YELLOW:
15544     case EL_EMERALD_RED:
15545     case EL_EMERALD_PURPLE:
15546     case EL_SP_INFOTRON:
15547       RaiseScore(level.score[SC_EMERALD]);
15548       break;
15549     case EL_DIAMOND:
15550       RaiseScore(level.score[SC_DIAMOND]);
15551       break;
15552     case EL_CRYSTAL:
15553       RaiseScore(level.score[SC_CRYSTAL]);
15554       break;
15555     case EL_PEARL:
15556       RaiseScore(level.score[SC_PEARL]);
15557       break;
15558     case EL_BUG:
15559     case EL_BD_BUTTERFLY:
15560     case EL_SP_ELECTRON:
15561       RaiseScore(level.score[SC_BUG]);
15562       break;
15563     case EL_SPACESHIP:
15564     case EL_BD_FIREFLY:
15565     case EL_SP_SNIKSNAK:
15566       RaiseScore(level.score[SC_SPACESHIP]);
15567       break;
15568     case EL_YAMYAM:
15569     case EL_DARK_YAMYAM:
15570       RaiseScore(level.score[SC_YAMYAM]);
15571       break;
15572     case EL_ROBOT:
15573       RaiseScore(level.score[SC_ROBOT]);
15574       break;
15575     case EL_PACMAN:
15576       RaiseScore(level.score[SC_PACMAN]);
15577       break;
15578     case EL_NUT:
15579       RaiseScore(level.score[SC_NUT]);
15580       break;
15581     case EL_DYNAMITE:
15582     case EL_EM_DYNAMITE:
15583     case EL_SP_DISK_RED:
15584     case EL_DYNABOMB_INCREASE_NUMBER:
15585     case EL_DYNABOMB_INCREASE_SIZE:
15586     case EL_DYNABOMB_INCREASE_POWER:
15587       RaiseScore(level.score[SC_DYNAMITE]);
15588       break;
15589     case EL_SHIELD_NORMAL:
15590     case EL_SHIELD_DEADLY:
15591       RaiseScore(level.score[SC_SHIELD]);
15592       break;
15593     case EL_EXTRA_TIME:
15594       RaiseScore(level.extra_time_score);
15595       break;
15596     case EL_KEY_1:
15597     case EL_KEY_2:
15598     case EL_KEY_3:
15599     case EL_KEY_4:
15600     case EL_EM_KEY_1:
15601     case EL_EM_KEY_2:
15602     case EL_EM_KEY_3:
15603     case EL_EM_KEY_4:
15604     case EL_EMC_KEY_5:
15605     case EL_EMC_KEY_6:
15606     case EL_EMC_KEY_7:
15607     case EL_EMC_KEY_8:
15608     case EL_DC_KEY_WHITE:
15609       RaiseScore(level.score[SC_KEY]);
15610       break;
15611     default:
15612       RaiseScore(element_info[element].collect_score);
15613       break;
15614   }
15615 }
15616
15617 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15618 {
15619   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15620   {
15621     if (!quick_quit)
15622     {
15623       // prevent short reactivation of overlay buttons while closing door
15624       SetOverlayActive(FALSE);
15625
15626       // door may still be open due to skipped or envelope style request
15627       CloseDoor(DOOR_CLOSE_1);
15628     }
15629
15630     if (network.enabled)
15631       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15632     else
15633     {
15634       if (quick_quit)
15635         FadeSkipNextFadeIn();
15636
15637       SetGameStatus(GAME_MODE_MAIN);
15638
15639       DrawMainMenu();
15640     }
15641   }
15642   else          // continue playing the game
15643   {
15644     if (tape.playing && tape.deactivate_display)
15645       TapeDeactivateDisplayOff(TRUE);
15646
15647     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15648
15649     if (tape.playing && tape.deactivate_display)
15650       TapeDeactivateDisplayOn();
15651   }
15652 }
15653
15654 void RequestQuitGame(boolean escape_key_pressed)
15655 {
15656   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15657   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15658                         level_editor_test_game);
15659   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15660                           quick_quit);
15661
15662   RequestQuitGameExt(skip_request, quick_quit,
15663                      "Do you really want to quit the game?");
15664 }
15665
15666 void RequestRestartGame(char *message)
15667 {
15668   game.restart_game_message = NULL;
15669
15670   boolean has_started_game = hasStartedNetworkGame();
15671   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15672
15673   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15674   {
15675     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15676   }
15677   else
15678   {
15679     // needed in case of envelope request to close game panel
15680     CloseDoor(DOOR_CLOSE_1);
15681
15682     SetGameStatus(GAME_MODE_MAIN);
15683
15684     DrawMainMenu();
15685   }
15686 }
15687
15688 void CheckGameOver(void)
15689 {
15690   static boolean last_game_over = FALSE;
15691   static int game_over_delay = 0;
15692   int game_over_delay_value = 50;
15693   boolean game_over = checkGameFailed();
15694
15695   // do not handle game over if request dialog is already active
15696   if (game.request_active)
15697     return;
15698
15699   // do not ask to play again if game was never actually played
15700   if (!game.GamePlayed)
15701     return;
15702
15703   if (!game_over)
15704   {
15705     last_game_over = FALSE;
15706     game_over_delay = game_over_delay_value;
15707
15708     return;
15709   }
15710
15711   if (game_over_delay > 0)
15712   {
15713     game_over_delay--;
15714
15715     return;
15716   }
15717
15718   if (last_game_over != game_over)
15719     game.restart_game_message = (hasStartedNetworkGame() ?
15720                                  "Game over! Play it again?" :
15721                                  "Game over!");
15722
15723   last_game_over = game_over;
15724 }
15725
15726 boolean checkGameSolved(void)
15727 {
15728   // set for all game engines if level was solved
15729   return game.LevelSolved_GameEnd;
15730 }
15731
15732 boolean checkGameFailed(void)
15733 {
15734   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15735     return (game_em.game_over && !game_em.level_solved);
15736   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15737     return (game_sp.game_over && !game_sp.level_solved);
15738   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15739     return (game_mm.game_over && !game_mm.level_solved);
15740   else                          // GAME_ENGINE_TYPE_RND
15741     return (game.GameOver && !game.LevelSolved);
15742 }
15743
15744 boolean checkGameEnded(void)
15745 {
15746   return (checkGameSolved() || checkGameFailed());
15747 }
15748
15749
15750 // ----------------------------------------------------------------------------
15751 // random generator functions
15752 // ----------------------------------------------------------------------------
15753
15754 unsigned int InitEngineRandom_RND(int seed)
15755 {
15756   game.num_random_calls = 0;
15757
15758   return InitEngineRandom(seed);
15759 }
15760
15761 unsigned int RND(int max)
15762 {
15763   if (max > 0)
15764   {
15765     game.num_random_calls++;
15766
15767     return GetEngineRandom(max);
15768   }
15769
15770   return 0;
15771 }
15772
15773
15774 // ----------------------------------------------------------------------------
15775 // game engine snapshot handling functions
15776 // ----------------------------------------------------------------------------
15777
15778 struct EngineSnapshotInfo
15779 {
15780   // runtime values for custom element collect score
15781   int collect_score[NUM_CUSTOM_ELEMENTS];
15782
15783   // runtime values for group element choice position
15784   int choice_pos[NUM_GROUP_ELEMENTS];
15785
15786   // runtime values for belt position animations
15787   int belt_graphic[4][NUM_BELT_PARTS];
15788   int belt_anim_mode[4][NUM_BELT_PARTS];
15789 };
15790
15791 static struct EngineSnapshotInfo engine_snapshot_rnd;
15792 static char *snapshot_level_identifier = NULL;
15793 static int snapshot_level_nr = -1;
15794
15795 static void SaveEngineSnapshotValues_RND(void)
15796 {
15797   static int belt_base_active_element[4] =
15798   {
15799     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15800     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15801     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15802     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15803   };
15804   int i, j;
15805
15806   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15807   {
15808     int element = EL_CUSTOM_START + i;
15809
15810     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15811   }
15812
15813   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15814   {
15815     int element = EL_GROUP_START + i;
15816
15817     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15818   }
15819
15820   for (i = 0; i < 4; i++)
15821   {
15822     for (j = 0; j < NUM_BELT_PARTS; j++)
15823     {
15824       int element = belt_base_active_element[i] + j;
15825       int graphic = el2img(element);
15826       int anim_mode = graphic_info[graphic].anim_mode;
15827
15828       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15829       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15830     }
15831   }
15832 }
15833
15834 static void LoadEngineSnapshotValues_RND(void)
15835 {
15836   unsigned int num_random_calls = game.num_random_calls;
15837   int i, j;
15838
15839   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15840   {
15841     int element = EL_CUSTOM_START + i;
15842
15843     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15844   }
15845
15846   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15847   {
15848     int element = EL_GROUP_START + i;
15849
15850     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15851   }
15852
15853   for (i = 0; i < 4; i++)
15854   {
15855     for (j = 0; j < NUM_BELT_PARTS; j++)
15856     {
15857       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15858       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15859
15860       graphic_info[graphic].anim_mode = anim_mode;
15861     }
15862   }
15863
15864   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15865   {
15866     InitRND(tape.random_seed);
15867     for (i = 0; i < num_random_calls; i++)
15868       RND(1);
15869   }
15870
15871   if (game.num_random_calls != num_random_calls)
15872   {
15873     Error("number of random calls out of sync");
15874     Error("number of random calls should be %d", num_random_calls);
15875     Error("number of random calls is %d", game.num_random_calls);
15876
15877     Fail("this should not happen -- please debug");
15878   }
15879 }
15880
15881 void FreeEngineSnapshotSingle(void)
15882 {
15883   FreeSnapshotSingle();
15884
15885   setString(&snapshot_level_identifier, NULL);
15886   snapshot_level_nr = -1;
15887 }
15888
15889 void FreeEngineSnapshotList(void)
15890 {
15891   FreeSnapshotList();
15892 }
15893
15894 static ListNode *SaveEngineSnapshotBuffers(void)
15895 {
15896   ListNode *buffers = NULL;
15897
15898   // copy some special values to a structure better suited for the snapshot
15899
15900   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15901     SaveEngineSnapshotValues_RND();
15902   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15903     SaveEngineSnapshotValues_EM();
15904   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15905     SaveEngineSnapshotValues_SP(&buffers);
15906   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15907     SaveEngineSnapshotValues_MM(&buffers);
15908
15909   // save values stored in special snapshot structure
15910
15911   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15912     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15913   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15914     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15915   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15916     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15917   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15918     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15919
15920   // save further RND engine values
15921
15922   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15923   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15924   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15925
15926   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15927   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15928   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15929   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15930   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15931
15932   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15933   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15934   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15935
15936   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15937
15938   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15939   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15940
15941   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15942   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15943   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15944   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15945   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15946   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15947   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15948   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15949   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15950   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15951   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15952   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15953   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15954   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15955   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15956   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15957   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15958   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15959
15960   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15961   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15962
15963   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15964   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15965   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15966
15967   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15968   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15969
15970   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15971   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15972   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
15973   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15974   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15975   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15976
15977   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15978   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15979
15980 #if 0
15981   ListNode *node = engine_snapshot_list_rnd;
15982   int num_bytes = 0;
15983
15984   while (node != NULL)
15985   {
15986     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15987
15988     node = node->next;
15989   }
15990
15991   Debug("game:playing:SaveEngineSnapshotBuffers",
15992         "size of engine snapshot: %d bytes", num_bytes);
15993 #endif
15994
15995   return buffers;
15996 }
15997
15998 void SaveEngineSnapshotSingle(void)
15999 {
16000   ListNode *buffers = SaveEngineSnapshotBuffers();
16001
16002   // finally save all snapshot buffers to single snapshot
16003   SaveSnapshotSingle(buffers);
16004
16005   // save level identification information
16006   setString(&snapshot_level_identifier, leveldir_current->identifier);
16007   snapshot_level_nr = level_nr;
16008 }
16009
16010 boolean CheckSaveEngineSnapshotToList(void)
16011 {
16012   boolean save_snapshot =
16013     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16014      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16015       game.snapshot.changed_action) ||
16016      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16017       game.snapshot.collected_item));
16018
16019   game.snapshot.changed_action = FALSE;
16020   game.snapshot.collected_item = FALSE;
16021   game.snapshot.save_snapshot = save_snapshot;
16022
16023   return save_snapshot;
16024 }
16025
16026 void SaveEngineSnapshotToList(void)
16027 {
16028   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16029       tape.quick_resume)
16030     return;
16031
16032   ListNode *buffers = SaveEngineSnapshotBuffers();
16033
16034   // finally save all snapshot buffers to snapshot list
16035   SaveSnapshotToList(buffers);
16036 }
16037
16038 void SaveEngineSnapshotToListInitial(void)
16039 {
16040   FreeEngineSnapshotList();
16041
16042   SaveEngineSnapshotToList();
16043 }
16044
16045 static void LoadEngineSnapshotValues(void)
16046 {
16047   // restore special values from snapshot structure
16048
16049   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16050     LoadEngineSnapshotValues_RND();
16051   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16052     LoadEngineSnapshotValues_EM();
16053   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16054     LoadEngineSnapshotValues_SP();
16055   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16056     LoadEngineSnapshotValues_MM();
16057 }
16058
16059 void LoadEngineSnapshotSingle(void)
16060 {
16061   LoadSnapshotSingle();
16062
16063   LoadEngineSnapshotValues();
16064 }
16065
16066 static void LoadEngineSnapshot_Undo(int steps)
16067 {
16068   LoadSnapshotFromList_Older(steps);
16069
16070   LoadEngineSnapshotValues();
16071 }
16072
16073 static void LoadEngineSnapshot_Redo(int steps)
16074 {
16075   LoadSnapshotFromList_Newer(steps);
16076
16077   LoadEngineSnapshotValues();
16078 }
16079
16080 boolean CheckEngineSnapshotSingle(void)
16081 {
16082   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16083           snapshot_level_nr == level_nr);
16084 }
16085
16086 boolean CheckEngineSnapshotList(void)
16087 {
16088   return CheckSnapshotList();
16089 }
16090
16091
16092 // ---------- new game button stuff -------------------------------------------
16093
16094 static struct
16095 {
16096   int graphic;
16097   struct XY *pos;
16098   int gadget_id;
16099   boolean *setup_value;
16100   boolean allowed_on_tape;
16101   boolean is_touch_button;
16102   char *infotext;
16103 } gamebutton_info[NUM_GAME_BUTTONS] =
16104 {
16105   {
16106     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16107     GAME_CTRL_ID_STOP,                          NULL,
16108     TRUE, FALSE,                                "stop game"
16109   },
16110   {
16111     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16112     GAME_CTRL_ID_PAUSE,                         NULL,
16113     TRUE, FALSE,                                "pause game"
16114   },
16115   {
16116     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16117     GAME_CTRL_ID_PLAY,                          NULL,
16118     TRUE, FALSE,                                "play game"
16119   },
16120   {
16121     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16122     GAME_CTRL_ID_UNDO,                          NULL,
16123     TRUE, FALSE,                                "undo step"
16124   },
16125   {
16126     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16127     GAME_CTRL_ID_REDO,                          NULL,
16128     TRUE, FALSE,                                "redo step"
16129   },
16130   {
16131     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16132     GAME_CTRL_ID_SAVE,                          NULL,
16133     TRUE, FALSE,                                "save game"
16134   },
16135   {
16136     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16137     GAME_CTRL_ID_PAUSE2,                        NULL,
16138     TRUE, FALSE,                                "pause game"
16139   },
16140   {
16141     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16142     GAME_CTRL_ID_LOAD,                          NULL,
16143     TRUE, FALSE,                                "load game"
16144   },
16145   {
16146     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16147     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16148     FALSE, FALSE,                               "stop game"
16149   },
16150   {
16151     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16152     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16153     FALSE, FALSE,                               "pause game"
16154   },
16155   {
16156     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16157     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16158     FALSE, FALSE,                               "play game"
16159   },
16160   {
16161     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16162     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16163     FALSE, TRUE,                                "stop game"
16164   },
16165   {
16166     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16167     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16168     FALSE, TRUE,                                "pause game"
16169   },
16170   {
16171     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16172     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16173     TRUE, FALSE,                                "background music on/off"
16174   },
16175   {
16176     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16177     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16178     TRUE, FALSE,                                "sound loops on/off"
16179   },
16180   {
16181     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16182     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16183     TRUE, FALSE,                                "normal sounds on/off"
16184   },
16185   {
16186     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16187     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16188     FALSE, FALSE,                               "background music on/off"
16189   },
16190   {
16191     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16192     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16193     FALSE, FALSE,                               "sound loops on/off"
16194   },
16195   {
16196     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16197     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16198     FALSE, FALSE,                               "normal sounds on/off"
16199   }
16200 };
16201
16202 void CreateGameButtons(void)
16203 {
16204   int i;
16205
16206   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16207   {
16208     int graphic = gamebutton_info[i].graphic;
16209     struct GraphicInfo *gfx = &graphic_info[graphic];
16210     struct XY *pos = gamebutton_info[i].pos;
16211     struct GadgetInfo *gi;
16212     int button_type;
16213     boolean checked;
16214     unsigned int event_mask;
16215     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16216     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16217     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16218     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16219     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16220     int gd_x   = gfx->src_x;
16221     int gd_y   = gfx->src_y;
16222     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16223     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16224     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16225     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16226     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16227     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16228     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16229     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16230     int id = i;
16231
16232     if (gfx->bitmap == NULL)
16233     {
16234       game_gadget[id] = NULL;
16235
16236       continue;
16237     }
16238
16239     if (id == GAME_CTRL_ID_STOP ||
16240         id == GAME_CTRL_ID_PANEL_STOP ||
16241         id == GAME_CTRL_ID_TOUCH_STOP ||
16242         id == GAME_CTRL_ID_PLAY ||
16243         id == GAME_CTRL_ID_PANEL_PLAY ||
16244         id == GAME_CTRL_ID_SAVE ||
16245         id == GAME_CTRL_ID_LOAD)
16246     {
16247       button_type = GD_TYPE_NORMAL_BUTTON;
16248       checked = FALSE;
16249       event_mask = GD_EVENT_RELEASED;
16250     }
16251     else if (id == GAME_CTRL_ID_UNDO ||
16252              id == GAME_CTRL_ID_REDO)
16253     {
16254       button_type = GD_TYPE_NORMAL_BUTTON;
16255       checked = FALSE;
16256       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16257     }
16258     else
16259     {
16260       button_type = GD_TYPE_CHECK_BUTTON;
16261       checked = (gamebutton_info[i].setup_value != NULL ?
16262                  *gamebutton_info[i].setup_value : FALSE);
16263       event_mask = GD_EVENT_PRESSED;
16264     }
16265
16266     gi = CreateGadget(GDI_CUSTOM_ID, id,
16267                       GDI_IMAGE_ID, graphic,
16268                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16269                       GDI_X, base_x + x,
16270                       GDI_Y, base_y + y,
16271                       GDI_WIDTH, gfx->width,
16272                       GDI_HEIGHT, gfx->height,
16273                       GDI_TYPE, button_type,
16274                       GDI_STATE, GD_BUTTON_UNPRESSED,
16275                       GDI_CHECKED, checked,
16276                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16277                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16278                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16279                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16280                       GDI_DIRECT_DRAW, FALSE,
16281                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16282                       GDI_EVENT_MASK, event_mask,
16283                       GDI_CALLBACK_ACTION, HandleGameButtons,
16284                       GDI_END);
16285
16286     if (gi == NULL)
16287       Fail("cannot create gadget");
16288
16289     game_gadget[id] = gi;
16290   }
16291 }
16292
16293 void FreeGameButtons(void)
16294 {
16295   int i;
16296
16297   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16298     FreeGadget(game_gadget[i]);
16299 }
16300
16301 static void UnmapGameButtonsAtSamePosition(int id)
16302 {
16303   int i;
16304
16305   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16306     if (i != id &&
16307         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16308         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16309       UnmapGadget(game_gadget[i]);
16310 }
16311
16312 static void UnmapGameButtonsAtSamePosition_All(void)
16313 {
16314   if (setup.show_load_save_buttons)
16315   {
16316     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16317     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16318     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16319   }
16320   else if (setup.show_undo_redo_buttons)
16321   {
16322     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16323     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16324     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16325   }
16326   else
16327   {
16328     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16329     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16330     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16331
16332     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16333     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16334     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16335   }
16336 }
16337
16338 void MapLoadSaveButtons(void)
16339 {
16340   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16341   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16342
16343   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16344   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16345 }
16346
16347 void MapUndoRedoButtons(void)
16348 {
16349   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16350   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16351
16352   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16353   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16354 }
16355
16356 void ModifyPauseButtons(void)
16357 {
16358   static int ids[] =
16359   {
16360     GAME_CTRL_ID_PAUSE,
16361     GAME_CTRL_ID_PAUSE2,
16362     GAME_CTRL_ID_PANEL_PAUSE,
16363     GAME_CTRL_ID_TOUCH_PAUSE,
16364     -1
16365   };
16366   int i;
16367
16368   for (i = 0; ids[i] > -1; i++)
16369     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16370 }
16371
16372 static void MapGameButtonsExt(boolean on_tape)
16373 {
16374   int i;
16375
16376   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16377     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16378       MapGadget(game_gadget[i]);
16379
16380   UnmapGameButtonsAtSamePosition_All();
16381
16382   RedrawGameButtons();
16383 }
16384
16385 static void UnmapGameButtonsExt(boolean on_tape)
16386 {
16387   int i;
16388
16389   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16390     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16391       UnmapGadget(game_gadget[i]);
16392 }
16393
16394 static void RedrawGameButtonsExt(boolean on_tape)
16395 {
16396   int i;
16397
16398   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16399     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16400       RedrawGadget(game_gadget[i]);
16401 }
16402
16403 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16404 {
16405   if (gi == NULL)
16406     return;
16407
16408   gi->checked = state;
16409 }
16410
16411 static void RedrawSoundButtonGadget(int id)
16412 {
16413   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16414              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16415              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16416              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16417              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16418              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16419              id);
16420
16421   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16422   RedrawGadget(game_gadget[id2]);
16423 }
16424
16425 void MapGameButtons(void)
16426 {
16427   MapGameButtonsExt(FALSE);
16428 }
16429
16430 void UnmapGameButtons(void)
16431 {
16432   UnmapGameButtonsExt(FALSE);
16433 }
16434
16435 void RedrawGameButtons(void)
16436 {
16437   RedrawGameButtonsExt(FALSE);
16438 }
16439
16440 void MapGameButtonsOnTape(void)
16441 {
16442   MapGameButtonsExt(TRUE);
16443 }
16444
16445 void UnmapGameButtonsOnTape(void)
16446 {
16447   UnmapGameButtonsExt(TRUE);
16448 }
16449
16450 void RedrawGameButtonsOnTape(void)
16451 {
16452   RedrawGameButtonsExt(TRUE);
16453 }
16454
16455 static void GameUndoRedoExt(void)
16456 {
16457   ClearPlayerAction();
16458
16459   tape.pausing = TRUE;
16460
16461   RedrawPlayfield();
16462   UpdateAndDisplayGameControlValues();
16463
16464   DrawCompleteVideoDisplay();
16465   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16466   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16467   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16468
16469   ModifyPauseButtons();
16470
16471   BackToFront();
16472 }
16473
16474 static void GameUndo(int steps)
16475 {
16476   if (!CheckEngineSnapshotList())
16477     return;
16478
16479   int tape_property_bits = tape.property_bits;
16480
16481   LoadEngineSnapshot_Undo(steps);
16482
16483   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16484
16485   GameUndoRedoExt();
16486 }
16487
16488 static void GameRedo(int steps)
16489 {
16490   if (!CheckEngineSnapshotList())
16491     return;
16492
16493   int tape_property_bits = tape.property_bits;
16494
16495   LoadEngineSnapshot_Redo(steps);
16496
16497   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16498
16499   GameUndoRedoExt();
16500 }
16501
16502 static void HandleGameButtonsExt(int id, int button)
16503 {
16504   static boolean game_undo_executed = FALSE;
16505   int steps = BUTTON_STEPSIZE(button);
16506   boolean handle_game_buttons =
16507     (game_status == GAME_MODE_PLAYING ||
16508      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16509
16510   if (!handle_game_buttons)
16511     return;
16512
16513   switch (id)
16514   {
16515     case GAME_CTRL_ID_STOP:
16516     case GAME_CTRL_ID_PANEL_STOP:
16517     case GAME_CTRL_ID_TOUCH_STOP:
16518       if (game_status == GAME_MODE_MAIN)
16519         break;
16520
16521       if (tape.playing)
16522         TapeStop();
16523       else
16524         RequestQuitGame(FALSE);
16525
16526       break;
16527
16528     case GAME_CTRL_ID_PAUSE:
16529     case GAME_CTRL_ID_PAUSE2:
16530     case GAME_CTRL_ID_PANEL_PAUSE:
16531     case GAME_CTRL_ID_TOUCH_PAUSE:
16532       if (network.enabled && game_status == GAME_MODE_PLAYING)
16533       {
16534         if (tape.pausing)
16535           SendToServer_ContinuePlaying();
16536         else
16537           SendToServer_PausePlaying();
16538       }
16539       else
16540         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16541
16542       game_undo_executed = FALSE;
16543
16544       break;
16545
16546     case GAME_CTRL_ID_PLAY:
16547     case GAME_CTRL_ID_PANEL_PLAY:
16548       if (game_status == GAME_MODE_MAIN)
16549       {
16550         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16551       }
16552       else if (tape.pausing)
16553       {
16554         if (network.enabled)
16555           SendToServer_ContinuePlaying();
16556         else
16557           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16558       }
16559       break;
16560
16561     case GAME_CTRL_ID_UNDO:
16562       // Important: When using "save snapshot when collecting an item" mode,
16563       // load last (current) snapshot for first "undo" after pressing "pause"
16564       // (else the last-but-one snapshot would be loaded, because the snapshot
16565       // pointer already points to the last snapshot when pressing "pause",
16566       // which is fine for "every step/move" mode, but not for "every collect")
16567       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16568           !game_undo_executed)
16569         steps--;
16570
16571       game_undo_executed = TRUE;
16572
16573       GameUndo(steps);
16574       break;
16575
16576     case GAME_CTRL_ID_REDO:
16577       GameRedo(steps);
16578       break;
16579
16580     case GAME_CTRL_ID_SAVE:
16581       TapeQuickSave();
16582       break;
16583
16584     case GAME_CTRL_ID_LOAD:
16585       TapeQuickLoad();
16586       break;
16587
16588     case SOUND_CTRL_ID_MUSIC:
16589     case SOUND_CTRL_ID_PANEL_MUSIC:
16590       if (setup.sound_music)
16591       { 
16592         setup.sound_music = FALSE;
16593
16594         FadeMusic();
16595       }
16596       else if (audio.music_available)
16597       { 
16598         setup.sound = setup.sound_music = TRUE;
16599
16600         SetAudioMode(setup.sound);
16601
16602         if (game_status == GAME_MODE_PLAYING)
16603           PlayLevelMusic();
16604       }
16605
16606       RedrawSoundButtonGadget(id);
16607
16608       break;
16609
16610     case SOUND_CTRL_ID_LOOPS:
16611     case SOUND_CTRL_ID_PANEL_LOOPS:
16612       if (setup.sound_loops)
16613         setup.sound_loops = FALSE;
16614       else if (audio.loops_available)
16615       {
16616         setup.sound = setup.sound_loops = TRUE;
16617
16618         SetAudioMode(setup.sound);
16619       }
16620
16621       RedrawSoundButtonGadget(id);
16622
16623       break;
16624
16625     case SOUND_CTRL_ID_SIMPLE:
16626     case SOUND_CTRL_ID_PANEL_SIMPLE:
16627       if (setup.sound_simple)
16628         setup.sound_simple = FALSE;
16629       else if (audio.sound_available)
16630       {
16631         setup.sound = setup.sound_simple = TRUE;
16632
16633         SetAudioMode(setup.sound);
16634       }
16635
16636       RedrawSoundButtonGadget(id);
16637
16638       break;
16639
16640     default:
16641       break;
16642   }
16643 }
16644
16645 static void HandleGameButtons(struct GadgetInfo *gi)
16646 {
16647   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16648 }
16649
16650 void HandleSoundButtonKeys(Key key)
16651 {
16652   if (key == setup.shortcut.sound_simple)
16653     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16654   else if (key == setup.shortcut.sound_loops)
16655     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16656   else if (key == setup.shortcut.sound_music)
16657     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16658 }