added using trigger element position for CE triggered global animations
[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 struct XY xy_topdown[] =
1558 {
1559   {  0, -1 },
1560   { -1,  0 },
1561   { +1,  0 },
1562   {  0, +1 }
1563 };
1564
1565 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1566
1567 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1568 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1569 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1570                                  IS_JUST_CHANGING(x, y))
1571
1572 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1573
1574 // static variables for playfield scan mode (scanning forward or backward)
1575 static int playfield_scan_start_x = 0;
1576 static int playfield_scan_start_y = 0;
1577 static int playfield_scan_delta_x = 1;
1578 static int playfield_scan_delta_y = 1;
1579
1580 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1581                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1582                                      (y) += playfield_scan_delta_y)     \
1583                                 for ((x) = playfield_scan_start_x;      \
1584                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1585                                      (x) += playfield_scan_delta_x)
1586
1587 #ifdef DEBUG
1588 void DEBUG_SetMaximumDynamite(void)
1589 {
1590   int i;
1591
1592   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1593     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1594       local_player->inventory_element[local_player->inventory_size++] =
1595         EL_DYNAMITE;
1596 }
1597 #endif
1598
1599 static void InitPlayfieldScanModeVars(void)
1600 {
1601   if (game.use_reverse_scan_direction)
1602   {
1603     playfield_scan_start_x = lev_fieldx - 1;
1604     playfield_scan_start_y = lev_fieldy - 1;
1605
1606     playfield_scan_delta_x = -1;
1607     playfield_scan_delta_y = -1;
1608   }
1609   else
1610   {
1611     playfield_scan_start_x = 0;
1612     playfield_scan_start_y = 0;
1613
1614     playfield_scan_delta_x = 1;
1615     playfield_scan_delta_y = 1;
1616   }
1617 }
1618
1619 static void InitPlayfieldScanMode(int mode)
1620 {
1621   game.use_reverse_scan_direction =
1622     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1623
1624   InitPlayfieldScanModeVars();
1625 }
1626
1627 static int get_move_delay_from_stepsize(int move_stepsize)
1628 {
1629   move_stepsize =
1630     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1631
1632   // make sure that stepsize value is always a power of 2
1633   move_stepsize = (1 << log_2(move_stepsize));
1634
1635   return TILEX / move_stepsize;
1636 }
1637
1638 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1639                                boolean init_game)
1640 {
1641   int player_nr = player->index_nr;
1642   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1643   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1644
1645   // do no immediately change move delay -- the player might just be moving
1646   player->move_delay_value_next = move_delay;
1647
1648   // information if player can move must be set separately
1649   player->cannot_move = cannot_move;
1650
1651   if (init_game)
1652   {
1653     player->move_delay       = game.initial_move_delay[player_nr];
1654     player->move_delay_value = game.initial_move_delay_value[player_nr];
1655
1656     player->move_delay_value_next = -1;
1657
1658     player->move_delay_reset_counter = 0;
1659   }
1660 }
1661
1662 void GetPlayerConfig(void)
1663 {
1664   GameFrameDelay = setup.game_frame_delay;
1665
1666   if (!audio.sound_available)
1667     setup.sound_simple = FALSE;
1668
1669   if (!audio.loops_available)
1670     setup.sound_loops = FALSE;
1671
1672   if (!audio.music_available)
1673     setup.sound_music = FALSE;
1674
1675   if (!video.fullscreen_available)
1676     setup.fullscreen = FALSE;
1677
1678   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1679
1680   SetAudioMode(setup.sound);
1681 }
1682
1683 int GetElementFromGroupElement(int element)
1684 {
1685   if (IS_GROUP_ELEMENT(element))
1686   {
1687     struct ElementGroupInfo *group = element_info[element].group;
1688     int last_anim_random_frame = gfx.anim_random_frame;
1689     int element_pos;
1690
1691     if (group->choice_mode == ANIM_RANDOM)
1692       gfx.anim_random_frame = RND(group->num_elements_resolved);
1693
1694     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1695                                     group->choice_mode, 0,
1696                                     group->choice_pos);
1697
1698     if (group->choice_mode == ANIM_RANDOM)
1699       gfx.anim_random_frame = last_anim_random_frame;
1700
1701     group->choice_pos++;
1702
1703     element = group->element_resolved[element_pos];
1704   }
1705
1706   return element;
1707 }
1708
1709 static void IncrementSokobanFieldsNeeded(void)
1710 {
1711   if (level.sb_fields_needed)
1712     game.sokoban_fields_still_needed++;
1713 }
1714
1715 static void IncrementSokobanObjectsNeeded(void)
1716 {
1717   if (level.sb_objects_needed)
1718     game.sokoban_objects_still_needed++;
1719 }
1720
1721 static void DecrementSokobanFieldsNeeded(void)
1722 {
1723   if (game.sokoban_fields_still_needed > 0)
1724     game.sokoban_fields_still_needed--;
1725 }
1726
1727 static void DecrementSokobanObjectsNeeded(void)
1728 {
1729   if (game.sokoban_objects_still_needed > 0)
1730     game.sokoban_objects_still_needed--;
1731 }
1732
1733 static void InitPlayerField(int x, int y, int element, boolean init_game)
1734 {
1735   if (element == EL_SP_MURPHY)
1736   {
1737     if (init_game)
1738     {
1739       if (stored_player[0].present)
1740       {
1741         Tile[x][y] = EL_SP_MURPHY_CLONE;
1742
1743         return;
1744       }
1745       else
1746       {
1747         stored_player[0].initial_element = element;
1748         stored_player[0].use_murphy = TRUE;
1749
1750         if (!level.use_artwork_element[0])
1751           stored_player[0].artwork_element = EL_SP_MURPHY;
1752       }
1753
1754       Tile[x][y] = EL_PLAYER_1;
1755     }
1756   }
1757
1758   if (init_game)
1759   {
1760     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1761     int jx = player->jx, jy = player->jy;
1762
1763     player->present = TRUE;
1764
1765     player->block_last_field = (element == EL_SP_MURPHY ?
1766                                 level.sp_block_last_field :
1767                                 level.block_last_field);
1768
1769     // ---------- initialize player's last field block delay ------------------
1770
1771     // always start with reliable default value (no adjustment needed)
1772     player->block_delay_adjustment = 0;
1773
1774     // special case 1: in Supaplex, Murphy blocks last field one more frame
1775     if (player->block_last_field && element == EL_SP_MURPHY)
1776       player->block_delay_adjustment = 1;
1777
1778     // special case 2: in game engines before 3.1.1, blocking was different
1779     if (game.use_block_last_field_bug)
1780       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1781
1782     if (!network.enabled || player->connected_network)
1783     {
1784       player->active = TRUE;
1785
1786       // remove potentially duplicate players
1787       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1788         StorePlayer[jx][jy] = 0;
1789
1790       StorePlayer[x][y] = Tile[x][y];
1791
1792 #if DEBUG_INIT_PLAYER
1793       Debug("game:init:player", "- player element %d activated",
1794             player->element_nr);
1795       Debug("game:init:player", "  (local player is %d and currently %s)",
1796             local_player->element_nr,
1797             local_player->active ? "active" : "not active");
1798     }
1799 #endif
1800
1801     Tile[x][y] = EL_EMPTY;
1802
1803     player->jx = player->last_jx = x;
1804     player->jy = player->last_jy = y;
1805   }
1806
1807   // always check if player was just killed and should be reanimated
1808   {
1809     int player_nr = GET_PLAYER_NR(element);
1810     struct PlayerInfo *player = &stored_player[player_nr];
1811
1812     if (player->active && player->killed)
1813       player->reanimated = TRUE; // if player was just killed, reanimate him
1814   }
1815 }
1816
1817 static void InitField(int x, int y, boolean init_game)
1818 {
1819   int element = Tile[x][y];
1820
1821   switch (element)
1822   {
1823     case EL_SP_MURPHY:
1824     case EL_PLAYER_1:
1825     case EL_PLAYER_2:
1826     case EL_PLAYER_3:
1827     case EL_PLAYER_4:
1828       InitPlayerField(x, y, element, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_PLAYER:
1832       element = Tile[x][y] = EL_PLAYER_1;
1833       InitField(x, y, init_game);
1834
1835       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1836       InitField(x, y, init_game);
1837       break;
1838
1839     case EL_SOKOBAN_FIELD_EMPTY:
1840       IncrementSokobanFieldsNeeded();
1841       break;
1842
1843     case EL_SOKOBAN_OBJECT:
1844       IncrementSokobanObjectsNeeded();
1845       break;
1846
1847     case EL_STONEBLOCK:
1848       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1849         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1850       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1851         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1852       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1853         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1854       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1855         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1856       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1857         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1858       break;
1859
1860     case EL_BUG:
1861     case EL_BUG_RIGHT:
1862     case EL_BUG_UP:
1863     case EL_BUG_LEFT:
1864     case EL_BUG_DOWN:
1865     case EL_SPACESHIP:
1866     case EL_SPACESHIP_RIGHT:
1867     case EL_SPACESHIP_UP:
1868     case EL_SPACESHIP_LEFT:
1869     case EL_SPACESHIP_DOWN:
1870     case EL_BD_BUTTERFLY:
1871     case EL_BD_BUTTERFLY_RIGHT:
1872     case EL_BD_BUTTERFLY_UP:
1873     case EL_BD_BUTTERFLY_LEFT:
1874     case EL_BD_BUTTERFLY_DOWN:
1875     case EL_BD_FIREFLY:
1876     case EL_BD_FIREFLY_RIGHT:
1877     case EL_BD_FIREFLY_UP:
1878     case EL_BD_FIREFLY_LEFT:
1879     case EL_BD_FIREFLY_DOWN:
1880     case EL_PACMAN_RIGHT:
1881     case EL_PACMAN_UP:
1882     case EL_PACMAN_LEFT:
1883     case EL_PACMAN_DOWN:
1884     case EL_YAMYAM:
1885     case EL_YAMYAM_LEFT:
1886     case EL_YAMYAM_RIGHT:
1887     case EL_YAMYAM_UP:
1888     case EL_YAMYAM_DOWN:
1889     case EL_DARK_YAMYAM:
1890     case EL_ROBOT:
1891     case EL_PACMAN:
1892     case EL_SP_SNIKSNAK:
1893     case EL_SP_ELECTRON:
1894     case EL_MOLE:
1895     case EL_MOLE_LEFT:
1896     case EL_MOLE_RIGHT:
1897     case EL_MOLE_UP:
1898     case EL_MOLE_DOWN:
1899     case EL_SPRING_LEFT:
1900     case EL_SPRING_RIGHT:
1901       InitMovDir(x, y);
1902       break;
1903
1904     case EL_AMOEBA_FULL:
1905     case EL_BD_AMOEBA:
1906       InitAmoebaNr(x, y);
1907       break;
1908
1909     case EL_AMOEBA_DROP:
1910       if (y == lev_fieldy - 1)
1911       {
1912         Tile[x][y] = EL_AMOEBA_GROWING;
1913         Store[x][y] = EL_AMOEBA_WET;
1914       }
1915       break;
1916
1917     case EL_DYNAMITE_ACTIVE:
1918     case EL_SP_DISK_RED_ACTIVE:
1919     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1920     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1921     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1922     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1923       MovDelay[x][y] = 96;
1924       break;
1925
1926     case EL_EM_DYNAMITE_ACTIVE:
1927       MovDelay[x][y] = 32;
1928       break;
1929
1930     case EL_LAMP:
1931       game.lights_still_needed++;
1932       break;
1933
1934     case EL_PENGUIN:
1935       game.friends_still_needed++;
1936       break;
1937
1938     case EL_PIG:
1939     case EL_DRAGON:
1940       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1941       break;
1942
1943     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1944     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1945     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1946     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1947     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1948     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1949     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1950     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1951     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1952     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1953     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1954     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1955       if (init_game)
1956       {
1957         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1958         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1959         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1960
1961         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1962         {
1963           game.belt_dir[belt_nr] = belt_dir;
1964           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1965         }
1966         else    // more than one switch -- set it like the first switch
1967         {
1968           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1969         }
1970       }
1971       break;
1972
1973     case EL_LIGHT_SWITCH_ACTIVE:
1974       if (init_game)
1975         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1976       break;
1977
1978     case EL_INVISIBLE_STEELWALL:
1979     case EL_INVISIBLE_WALL:
1980     case EL_INVISIBLE_SAND:
1981       if (game.light_time_left > 0 ||
1982           game.lenses_time_left > 0)
1983         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1984       break;
1985
1986     case EL_EMC_MAGIC_BALL:
1987       if (game.ball_active)
1988         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1989       break;
1990
1991     case EL_EMC_MAGIC_BALL_SWITCH:
1992       if (game.ball_active)
1993         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1994       break;
1995
1996     case EL_TRIGGER_PLAYER:
1997     case EL_TRIGGER_ELEMENT:
1998     case EL_TRIGGER_CE_VALUE:
1999     case EL_TRIGGER_CE_SCORE:
2000     case EL_SELF:
2001     case EL_ANY_ELEMENT:
2002     case EL_CURRENT_CE_VALUE:
2003     case EL_CURRENT_CE_SCORE:
2004     case EL_PREV_CE_1:
2005     case EL_PREV_CE_2:
2006     case EL_PREV_CE_3:
2007     case EL_PREV_CE_4:
2008     case EL_PREV_CE_5:
2009     case EL_PREV_CE_6:
2010     case EL_PREV_CE_7:
2011     case EL_PREV_CE_8:
2012     case EL_NEXT_CE_1:
2013     case EL_NEXT_CE_2:
2014     case EL_NEXT_CE_3:
2015     case EL_NEXT_CE_4:
2016     case EL_NEXT_CE_5:
2017     case EL_NEXT_CE_6:
2018     case EL_NEXT_CE_7:
2019     case EL_NEXT_CE_8:
2020       // reference elements should not be used on the playfield
2021       Tile[x][y] = EL_EMPTY;
2022       break;
2023
2024     default:
2025       if (IS_CUSTOM_ELEMENT(element))
2026       {
2027         if (CAN_MOVE(element))
2028           InitMovDir(x, y);
2029
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2032       }
2033       else if (IS_GROUP_ELEMENT(element))
2034       {
2035         Tile[x][y] = GetElementFromGroupElement(element);
2036
2037         InitField(x, y, init_game);
2038       }
2039       else if (IS_EMPTY_ELEMENT(element))
2040       {
2041         GfxElementEmpty[x][y] = element;
2042         Tile[x][y] = EL_EMPTY;
2043
2044         if (element_info[element].use_gfx_element)
2045           game.use_masked_elements = TRUE;
2046       }
2047
2048       break;
2049   }
2050
2051   if (!init_game)
2052     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2053 }
2054
2055 static void InitField_WithBug1(int x, int y, boolean init_game)
2056 {
2057   InitField(x, y, init_game);
2058
2059   // not needed to call InitMovDir() -- already done by InitField()!
2060   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2061       CAN_MOVE(Tile[x][y]))
2062     InitMovDir(x, y);
2063 }
2064
2065 static void InitField_WithBug2(int x, int y, boolean init_game)
2066 {
2067   int old_element = Tile[x][y];
2068
2069   InitField(x, y, init_game);
2070
2071   // not needed to call InitMovDir() -- already done by InitField()!
2072   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2073       CAN_MOVE(old_element) &&
2074       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2075     InitMovDir(x, y);
2076
2077   /* this case is in fact a combination of not less than three bugs:
2078      first, it calls InitMovDir() for elements that can move, although this is
2079      already done by InitField(); then, it checks the element that was at this
2080      field _before_ the call to InitField() (which can change it); lastly, it
2081      was not called for "mole with direction" elements, which were treated as
2082      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2083   */
2084 }
2085
2086 static int get_key_element_from_nr(int key_nr)
2087 {
2088   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2089                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2090                           EL_EM_KEY_1 : EL_KEY_1);
2091
2092   return key_base_element + key_nr;
2093 }
2094
2095 static int get_next_dropped_element(struct PlayerInfo *player)
2096 {
2097   return (player->inventory_size > 0 ?
2098           player->inventory_element[player->inventory_size - 1] :
2099           player->inventory_infinite_element != EL_UNDEFINED ?
2100           player->inventory_infinite_element :
2101           player->dynabombs_left > 0 ?
2102           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2103           EL_UNDEFINED);
2104 }
2105
2106 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2107 {
2108   // pos >= 0: get element from bottom of the stack;
2109   // pos <  0: get element from top of the stack
2110
2111   if (pos < 0)
2112   {
2113     int min_inventory_size = -pos;
2114     int inventory_pos = player->inventory_size - min_inventory_size;
2115     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2116
2117     return (player->inventory_size >= min_inventory_size ?
2118             player->inventory_element[inventory_pos] :
2119             player->inventory_infinite_element != EL_UNDEFINED ?
2120             player->inventory_infinite_element :
2121             player->dynabombs_left >= min_dynabombs_left ?
2122             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2123             EL_UNDEFINED);
2124   }
2125   else
2126   {
2127     int min_dynabombs_left = pos + 1;
2128     int min_inventory_size = pos + 1 - player->dynabombs_left;
2129     int inventory_pos = pos - player->dynabombs_left;
2130
2131     return (player->inventory_infinite_element != EL_UNDEFINED ?
2132             player->inventory_infinite_element :
2133             player->dynabombs_left >= min_dynabombs_left ?
2134             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2135             player->inventory_size >= min_inventory_size ?
2136             player->inventory_element[inventory_pos] :
2137             EL_UNDEFINED);
2138   }
2139 }
2140
2141 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2142 {
2143   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2144   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2145   int compare_result;
2146
2147   if (gpo1->sort_priority != gpo2->sort_priority)
2148     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2149   else
2150     compare_result = gpo1->nr - gpo2->nr;
2151
2152   return compare_result;
2153 }
2154
2155 int getPlayerInventorySize(int player_nr)
2156 {
2157   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2158     return game_em.ply[player_nr]->dynamite;
2159   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2160     return game_sp.red_disk_count;
2161   else
2162     return stored_player[player_nr].inventory_size;
2163 }
2164
2165 static void InitGameControlValues(void)
2166 {
2167   int i;
2168
2169   for (i = 0; game_panel_controls[i].nr != -1; i++)
2170   {
2171     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2172     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2173     struct TextPosInfo *pos = gpc->pos;
2174     int nr = gpc->nr;
2175     int type = gpc->type;
2176
2177     if (nr != i)
2178     {
2179       Error("'game_panel_controls' structure corrupted at %d", i);
2180
2181       Fail("this should not happen -- please debug");
2182     }
2183
2184     // force update of game controls after initialization
2185     gpc->value = gpc->last_value = -1;
2186     gpc->frame = gpc->last_frame = -1;
2187     gpc->gfx_frame = -1;
2188
2189     // determine panel value width for later calculation of alignment
2190     if (type == TYPE_INTEGER || type == TYPE_STRING)
2191     {
2192       pos->width = pos->size * getFontWidth(pos->font);
2193       pos->height = getFontHeight(pos->font);
2194     }
2195     else if (type == TYPE_ELEMENT)
2196     {
2197       pos->width = pos->size;
2198       pos->height = pos->size;
2199     }
2200
2201     // fill structure for game panel draw order
2202     gpo->nr = gpc->nr;
2203     gpo->sort_priority = pos->sort_priority;
2204   }
2205
2206   // sort game panel controls according to sort_priority and control number
2207   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2208         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2209 }
2210
2211 static void UpdatePlayfieldElementCount(void)
2212 {
2213   boolean use_element_count = FALSE;
2214   int i, j, x, y;
2215
2216   // first check if it is needed at all to calculate playfield element count
2217   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2218     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2219       use_element_count = TRUE;
2220
2221   if (!use_element_count)
2222     return;
2223
2224   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2225     element_info[i].element_count = 0;
2226
2227   SCAN_PLAYFIELD(x, y)
2228   {
2229     element_info[Tile[x][y]].element_count++;
2230   }
2231
2232   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2233     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2234       if (IS_IN_GROUP(j, i))
2235         element_info[EL_GROUP_START + i].element_count +=
2236           element_info[j].element_count;
2237 }
2238
2239 static void UpdateGameControlValues(void)
2240 {
2241   int i, k;
2242   int time = (game.LevelSolved ?
2243               game.LevelSolved_CountingTime :
2244               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2245               game_em.lev->time :
2246               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2247               game_sp.time_played :
2248               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249               game_mm.energy_left :
2250               game.no_level_time_limit ? TimePlayed : TimeLeft);
2251   int score = (game.LevelSolved ?
2252                game.LevelSolved_CountingScore :
2253                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2254                game_em.lev->score :
2255                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2256                game_sp.score :
2257                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2258                game_mm.score :
2259                game.score);
2260   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2261               game_em.lev->gems_needed :
2262               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2263               game_sp.infotrons_still_needed :
2264               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2265               game_mm.kettles_still_needed :
2266               game.gems_still_needed);
2267   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2268                      game_em.lev->gems_needed > 0 :
2269                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2270                      game_sp.infotrons_still_needed > 0 :
2271                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2272                      game_mm.kettles_still_needed > 0 ||
2273                      game_mm.lights_still_needed > 0 :
2274                      game.gems_still_needed > 0 ||
2275                      game.sokoban_fields_still_needed > 0 ||
2276                      game.sokoban_objects_still_needed > 0 ||
2277                      game.lights_still_needed > 0);
2278   int health = (game.LevelSolved ?
2279                 game.LevelSolved_CountingHealth :
2280                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2281                 MM_HEALTH(game_mm.laser_overload_value) :
2282                 game.health);
2283   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2284
2285   UpdatePlayfieldElementCount();
2286
2287   // update game panel control values
2288
2289   // used instead of "level_nr" (for network games)
2290   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2291   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2292
2293   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2294   for (i = 0; i < MAX_NUM_KEYS; i++)
2295     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2296   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2297   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2298
2299   if (game.centered_player_nr == -1)
2300   {
2301     for (i = 0; i < MAX_PLAYERS; i++)
2302     {
2303       // only one player in Supaplex game engine
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2305         break;
2306
2307       for (k = 0; k < MAX_NUM_KEYS; k++)
2308       {
2309         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2310         {
2311           if (game_em.ply[i]->keys & (1 << k))
2312             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2313               get_key_element_from_nr(k);
2314         }
2315         else if (stored_player[i].key[k])
2316           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2317             get_key_element_from_nr(k);
2318       }
2319
2320       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2321         getPlayerInventorySize(i);
2322
2323       if (stored_player[i].num_white_keys > 0)
2324         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2325           EL_DC_KEY_WHITE;
2326
2327       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328         stored_player[i].num_white_keys;
2329     }
2330   }
2331   else
2332   {
2333     int player_nr = game.centered_player_nr;
2334
2335     for (k = 0; k < MAX_NUM_KEYS; k++)
2336     {
2337       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2338       {
2339         if (game_em.ply[player_nr]->keys & (1 << k))
2340           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2341             get_key_element_from_nr(k);
2342       }
2343       else if (stored_player[player_nr].key[k])
2344         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2345           get_key_element_from_nr(k);
2346     }
2347
2348     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2349       getPlayerInventorySize(player_nr);
2350
2351     if (stored_player[player_nr].num_white_keys > 0)
2352       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2353
2354     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2355       stored_player[player_nr].num_white_keys;
2356   }
2357
2358   // re-arrange keys on game panel, if needed or if defined by style settings
2359   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2360   {
2361     int nr = GAME_PANEL_KEY_1 + i;
2362     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2363     struct TextPosInfo *pos = gpc->pos;
2364
2365     // skip check if key is not in the player's inventory
2366     if (gpc->value == EL_EMPTY)
2367       continue;
2368
2369     // check if keys should be arranged on panel from left to right
2370     if (pos->style == STYLE_LEFTMOST_POSITION)
2371     {
2372       // check previous key positions (left from current key)
2373       for (k = 0; k < i; k++)
2374       {
2375         int nr_new = GAME_PANEL_KEY_1 + k;
2376
2377         if (game_panel_controls[nr_new].value == EL_EMPTY)
2378         {
2379           game_panel_controls[nr_new].value = gpc->value;
2380           gpc->value = EL_EMPTY;
2381
2382           break;
2383         }
2384       }
2385     }
2386
2387     // check if "undefined" keys can be placed at some other position
2388     if (pos->x == -1 && pos->y == -1)
2389     {
2390       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2391
2392       // 1st try: display key at the same position as normal or EM keys
2393       if (game_panel_controls[nr_new].value == EL_EMPTY)
2394       {
2395         game_panel_controls[nr_new].value = gpc->value;
2396       }
2397       else
2398       {
2399         // 2nd try: display key at the next free position in the key panel
2400         for (k = 0; k < STD_NUM_KEYS; k++)
2401         {
2402           nr_new = GAME_PANEL_KEY_1 + k;
2403
2404           if (game_panel_controls[nr_new].value == EL_EMPTY)
2405           {
2406             game_panel_controls[nr_new].value = gpc->value;
2407
2408             break;
2409           }
2410         }
2411       }
2412     }
2413   }
2414
2415   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2416   {
2417     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2418       get_inventory_element_from_pos(local_player, i);
2419     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2420       get_inventory_element_from_pos(local_player, -i - 1);
2421   }
2422
2423   game_panel_controls[GAME_PANEL_SCORE].value = score;
2424   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2425
2426   game_panel_controls[GAME_PANEL_TIME].value = time;
2427
2428   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2429   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2430   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2431
2432   if (level.time == 0)
2433     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2434   else
2435     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2436
2437   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2438   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2439
2440   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2441
2442   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2443     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2444      EL_EMPTY);
2445   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2446     local_player->shield_normal_time_left;
2447   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2448     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2449      EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2451     local_player->shield_deadly_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EXIT].value =
2454     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2455
2456   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2457     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2458   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2459     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2460      EL_EMC_MAGIC_BALL_SWITCH);
2461
2462   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2463     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2464   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2465     game.light_time_left;
2466
2467   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2468     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2469   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2470     game.timegate_time_left;
2471
2472   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2473     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2474
2475   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2476     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2477   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2478     game.lenses_time_left;
2479
2480   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2481     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2482   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2483     game.magnify_time_left;
2484
2485   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2486     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2487      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2488      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2489      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2490      EL_BALLOON_SWITCH_NONE);
2491
2492   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2493     local_player->dynabomb_count;
2494   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2495     local_player->dynabomb_size;
2496   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2497     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2498
2499   game_panel_controls[GAME_PANEL_PENGUINS].value =
2500     game.friends_still_needed;
2501
2502   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2503     game.sokoban_objects_still_needed;
2504   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2505     game.sokoban_fields_still_needed;
2506
2507   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2508     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2509
2510   for (i = 0; i < NUM_BELTS; i++)
2511   {
2512     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2513       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2514        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2515     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2516       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2517   }
2518
2519   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2520     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2521   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2522     game.magic_wall_time_left;
2523
2524   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2525     local_player->gravity;
2526
2527   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2528     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2529
2530   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2531     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2532       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2533        game.panel.element[i].id : EL_UNDEFINED);
2534
2535   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2536     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2537       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2538        element_info[game.panel.element_count[i].id].element_count : 0);
2539
2540   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2541     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2542       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2543        element_info[game.panel.ce_score[i].id].collect_score : 0);
2544
2545   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2546     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2547       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2548        element_info[game.panel.ce_score_element[i].id].collect_score :
2549        EL_UNDEFINED);
2550
2551   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2552   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2553   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2554
2555   // update game panel control frames
2556
2557   for (i = 0; game_panel_controls[i].nr != -1; i++)
2558   {
2559     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2560
2561     if (gpc->type == TYPE_ELEMENT)
2562     {
2563       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2564       {
2565         int last_anim_random_frame = gfx.anim_random_frame;
2566         int element = gpc->value;
2567         int graphic = el2panelimg(element);
2568         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2569                                sync_random_frame :
2570                                graphic_info[graphic].anim_global_anim_sync ?
2571                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2572
2573         if (gpc->value != gpc->last_value)
2574         {
2575           gpc->gfx_frame = 0;
2576           gpc->gfx_random = init_gfx_random;
2577         }
2578         else
2579         {
2580           gpc->gfx_frame++;
2581
2582           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2583               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2584             gpc->gfx_random = init_gfx_random;
2585         }
2586
2587         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2588           gfx.anim_random_frame = gpc->gfx_random;
2589
2590         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2591           gpc->gfx_frame = element_info[element].collect_score;
2592
2593         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2594
2595         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2596           gfx.anim_random_frame = last_anim_random_frame;
2597       }
2598     }
2599     else if (gpc->type == TYPE_GRAPHIC)
2600     {
2601       if (gpc->graphic != IMG_UNDEFINED)
2602       {
2603         int last_anim_random_frame = gfx.anim_random_frame;
2604         int graphic = gpc->graphic;
2605         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2606                                sync_random_frame :
2607                                graphic_info[graphic].anim_global_anim_sync ?
2608                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2609
2610         if (gpc->value != gpc->last_value)
2611         {
2612           gpc->gfx_frame = 0;
2613           gpc->gfx_random = init_gfx_random;
2614         }
2615         else
2616         {
2617           gpc->gfx_frame++;
2618
2619           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2620               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2621             gpc->gfx_random = init_gfx_random;
2622         }
2623
2624         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2625           gfx.anim_random_frame = gpc->gfx_random;
2626
2627         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2628
2629         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2630           gfx.anim_random_frame = last_anim_random_frame;
2631       }
2632     }
2633   }
2634 }
2635
2636 static void DisplayGameControlValues(void)
2637 {
2638   boolean redraw_panel = FALSE;
2639   int i;
2640
2641   for (i = 0; game_panel_controls[i].nr != -1; i++)
2642   {
2643     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2644
2645     if (PANEL_DEACTIVATED(gpc->pos))
2646       continue;
2647
2648     if (gpc->value == gpc->last_value &&
2649         gpc->frame == gpc->last_frame)
2650       continue;
2651
2652     redraw_panel = TRUE;
2653   }
2654
2655   if (!redraw_panel)
2656     return;
2657
2658   // copy default game door content to main double buffer
2659
2660   // !!! CHECK AGAIN !!!
2661   SetPanelBackground();
2662   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2663   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2664
2665   // redraw game control buttons
2666   RedrawGameButtons();
2667
2668   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2669
2670   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2671   {
2672     int nr = game_panel_order[i].nr;
2673     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2674     struct TextPosInfo *pos = gpc->pos;
2675     int type = gpc->type;
2676     int value = gpc->value;
2677     int frame = gpc->frame;
2678     int size = pos->size;
2679     int font = pos->font;
2680     boolean draw_masked = pos->draw_masked;
2681     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2682
2683     if (PANEL_DEACTIVATED(pos))
2684       continue;
2685
2686     if (pos->class == get_hash_from_key("extra_panel_items") &&
2687         !setup.prefer_extra_panel_items)
2688       continue;
2689
2690     gpc->last_value = value;
2691     gpc->last_frame = frame;
2692
2693     if (type == TYPE_INTEGER)
2694     {
2695       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2696           nr == GAME_PANEL_INVENTORY_COUNT ||
2697           nr == GAME_PANEL_SCORE ||
2698           nr == GAME_PANEL_HIGHSCORE ||
2699           nr == GAME_PANEL_TIME)
2700       {
2701         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2702
2703         if (use_dynamic_size)           // use dynamic number of digits
2704         {
2705           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2706                               nr == GAME_PANEL_INVENTORY_COUNT ||
2707                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2708           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2709                           nr == GAME_PANEL_INVENTORY_COUNT ||
2710                           nr == GAME_PANEL_TIME ? 1 : 2);
2711           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2712                        nr == GAME_PANEL_INVENTORY_COUNT ||
2713                        nr == GAME_PANEL_TIME ? 3 : 5);
2714           int size2 = size1 + size_add;
2715           int font1 = pos->font;
2716           int font2 = pos->font_alt;
2717
2718           size = (value < value_change ? size1 : size2);
2719           font = (value < value_change ? font1 : font2);
2720         }
2721       }
2722
2723       // correct text size if "digits" is zero or less
2724       if (size <= 0)
2725         size = strlen(int2str(value, size));
2726
2727       // dynamically correct text alignment
2728       pos->width = size * getFontWidth(font);
2729
2730       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2731                   int2str(value, size), font, mask_mode);
2732     }
2733     else if (type == TYPE_ELEMENT)
2734     {
2735       int element, graphic;
2736       Bitmap *src_bitmap;
2737       int src_x, src_y;
2738       int width, height;
2739       int dst_x = PANEL_XPOS(pos);
2740       int dst_y = PANEL_YPOS(pos);
2741
2742       if (value != EL_UNDEFINED && value != EL_EMPTY)
2743       {
2744         element = value;
2745         graphic = el2panelimg(value);
2746
2747 #if 0
2748         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2749               element, EL_NAME(element), size);
2750 #endif
2751
2752         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2753           size = TILESIZE;
2754
2755         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2756                               &src_x, &src_y);
2757
2758         width  = graphic_info[graphic].width  * size / TILESIZE;
2759         height = graphic_info[graphic].height * size / TILESIZE;
2760
2761         if (draw_masked)
2762           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2763                            dst_x, dst_y);
2764         else
2765           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2766                      dst_x, dst_y);
2767       }
2768     }
2769     else if (type == TYPE_GRAPHIC)
2770     {
2771       int graphic        = gpc->graphic;
2772       int graphic_active = gpc->graphic_active;
2773       Bitmap *src_bitmap;
2774       int src_x, src_y;
2775       int width, height;
2776       int dst_x = PANEL_XPOS(pos);
2777       int dst_y = PANEL_YPOS(pos);
2778       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2779                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2780
2781       if (graphic != IMG_UNDEFINED && !skip)
2782       {
2783         if (pos->style == STYLE_REVERSE)
2784           value = 100 - value;
2785
2786         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2787
2788         if (pos->direction & MV_HORIZONTAL)
2789         {
2790           width  = graphic_info[graphic_active].width * value / 100;
2791           height = graphic_info[graphic_active].height;
2792
2793           if (pos->direction == MV_LEFT)
2794           {
2795             src_x += graphic_info[graphic_active].width - width;
2796             dst_x += graphic_info[graphic_active].width - width;
2797           }
2798         }
2799         else
2800         {
2801           width  = graphic_info[graphic_active].width;
2802           height = graphic_info[graphic_active].height * value / 100;
2803
2804           if (pos->direction == MV_UP)
2805           {
2806             src_y += graphic_info[graphic_active].height - height;
2807             dst_y += graphic_info[graphic_active].height - height;
2808           }
2809         }
2810
2811         if (draw_masked)
2812           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2813                            dst_x, dst_y);
2814         else
2815           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2816                      dst_x, dst_y);
2817
2818         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2819
2820         if (pos->direction & MV_HORIZONTAL)
2821         {
2822           if (pos->direction == MV_RIGHT)
2823           {
2824             src_x += width;
2825             dst_x += width;
2826           }
2827           else
2828           {
2829             dst_x = PANEL_XPOS(pos);
2830           }
2831
2832           width = graphic_info[graphic].width - width;
2833         }
2834         else
2835         {
2836           if (pos->direction == MV_DOWN)
2837           {
2838             src_y += height;
2839             dst_y += height;
2840           }
2841           else
2842           {
2843             dst_y = PANEL_YPOS(pos);
2844           }
2845
2846           height = graphic_info[graphic].height - height;
2847         }
2848
2849         if (draw_masked)
2850           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2851                            dst_x, dst_y);
2852         else
2853           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2854                      dst_x, dst_y);
2855       }
2856     }
2857     else if (type == TYPE_STRING)
2858     {
2859       boolean active = (value != 0);
2860       char *state_normal = "off";
2861       char *state_active = "on";
2862       char *state = (active ? state_active : state_normal);
2863       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2864                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2865                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2866                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2867
2868       if (nr == GAME_PANEL_GRAVITY_STATE)
2869       {
2870         int font1 = pos->font;          // (used for normal state)
2871         int font2 = pos->font_alt;      // (used for active state)
2872
2873         font = (active ? font2 : font1);
2874       }
2875
2876       if (s != NULL)
2877       {
2878         char *s_cut;
2879
2880         if (size <= 0)
2881         {
2882           // don't truncate output if "chars" is zero or less
2883           size = strlen(s);
2884
2885           // dynamically correct text alignment
2886           pos->width = size * getFontWidth(font);
2887         }
2888
2889         s_cut = getStringCopyN(s, size);
2890
2891         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2892                     s_cut, font, mask_mode);
2893
2894         free(s_cut);
2895       }
2896     }
2897
2898     redraw_mask |= REDRAW_DOOR_1;
2899   }
2900
2901   SetGameStatus(GAME_MODE_PLAYING);
2902 }
2903
2904 void UpdateAndDisplayGameControlValues(void)
2905 {
2906   if (tape.deactivate_display)
2907     return;
2908
2909   UpdateGameControlValues();
2910   DisplayGameControlValues();
2911 }
2912
2913 void UpdateGameDoorValues(void)
2914 {
2915   UpdateGameControlValues();
2916 }
2917
2918 void DrawGameDoorValues(void)
2919 {
2920   DisplayGameControlValues();
2921 }
2922
2923
2924 // ============================================================================
2925 // InitGameEngine()
2926 // ----------------------------------------------------------------------------
2927 // initialize game engine due to level / tape version number
2928 // ============================================================================
2929
2930 static void InitGameEngine(void)
2931 {
2932   int i, j, k, l, x, y;
2933
2934   // set game engine from tape file when re-playing, else from level file
2935   game.engine_version = (tape.playing ? tape.engine_version :
2936                          level.game_version);
2937
2938   // set single or multi-player game mode (needed for re-playing tapes)
2939   game.team_mode = setup.team_mode;
2940
2941   if (tape.playing)
2942   {
2943     int num_players = 0;
2944
2945     for (i = 0; i < MAX_PLAYERS; i++)
2946       if (tape.player_participates[i])
2947         num_players++;
2948
2949     // multi-player tapes contain input data for more than one player
2950     game.team_mode = (num_players > 1);
2951   }
2952
2953 #if 0
2954   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2955         level.game_version);
2956   Debug("game:init:level", "          tape.file_version   == %06d",
2957         tape.file_version);
2958   Debug("game:init:level", "          tape.game_version   == %06d",
2959         tape.game_version);
2960   Debug("game:init:level", "          tape.engine_version == %06d",
2961         tape.engine_version);
2962   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2963         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2964 #endif
2965
2966   // --------------------------------------------------------------------------
2967   // set flags for bugs and changes according to active game engine version
2968   // --------------------------------------------------------------------------
2969
2970   /*
2971     Summary of bugfix:
2972     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2973
2974     Bug was introduced in version:
2975     2.0.1
2976
2977     Bug was fixed in version:
2978     4.2.0.0
2979
2980     Description:
2981     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2982     but the property "can fall" was missing, which caused some levels to be
2983     unsolvable. This was fixed in version 4.2.0.0.
2984
2985     Affected levels/tapes:
2986     An example for a tape that was fixed by this bugfix is tape 029 from the
2987     level set "rnd_sam_bateman".
2988     The wrong behaviour will still be used for all levels or tapes that were
2989     created/recorded with it. An example for this is tape 023 from the level
2990     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2991   */
2992
2993   boolean use_amoeba_dropping_cannot_fall_bug =
2994     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2995       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2996      (tape.playing &&
2997       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2998       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2999
3000   /*
3001     Summary of bugfix/change:
3002     Fixed move speed of elements entering or leaving magic wall.
3003
3004     Fixed/changed in version:
3005     2.0.1
3006
3007     Description:
3008     Before 2.0.1, move speed of elements entering or leaving magic wall was
3009     twice as fast as it is now.
3010     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3011
3012     Affected levels/tapes:
3013     The first condition is generally needed for all levels/tapes before version
3014     2.0.1, which might use the old behaviour before it was changed; known tapes
3015     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3018     above, but before it was known that this change would break tapes like the
3019     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3020     although the engine version while recording maybe was before 2.0.1. There
3021     are a lot of tapes that are affected by this exception, like tape 006 from
3022     the level set "rnd_conor_mancone".
3023   */
3024
3025   boolean use_old_move_stepsize_for_magic_wall =
3026     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3027      !(tape.playing &&
3028        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3029        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3030
3031   /*
3032     Summary of bugfix/change:
3033     Fixed handling for custom elements that change when pushed by the player.
3034
3035     Fixed/changed in version:
3036     3.1.0
3037
3038     Description:
3039     Before 3.1.0, custom elements that "change when pushing" changed directly
3040     after the player started pushing them (until then handled in "DigField()").
3041     Since 3.1.0, these custom elements are not changed until the "pushing"
3042     move of the element is finished (now handled in "ContinueMoving()").
3043
3044     Affected levels/tapes:
3045     The first condition is generally needed for all levels/tapes before version
3046     3.1.0, which might use the old behaviour before it was changed; known tapes
3047     that are affected are some tapes from the level set "Walpurgis Gardens" by
3048     Jamie Cullen.
3049     The second condition is an exception from the above case and is needed for
3050     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3051     above (including some development versions of 3.1.0), but before it was
3052     known that this change would break tapes like the above and was fixed in
3053     3.1.1, so that the changed behaviour was active although the engine version
3054     while recording maybe was before 3.1.0. There is at least one tape that is
3055     affected by this exception, which is the tape for the one-level set "Bug
3056     Machine" by Juergen Bonhagen.
3057   */
3058
3059   game.use_change_when_pushing_bug =
3060     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3061      !(tape.playing &&
3062        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3063        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3064
3065   /*
3066     Summary of bugfix/change:
3067     Fixed handling for blocking the field the player leaves when moving.
3068
3069     Fixed/changed in version:
3070     3.1.1
3071
3072     Description:
3073     Before 3.1.1, when "block last field when moving" was enabled, the field
3074     the player is leaving when moving was blocked for the time of the move,
3075     and was directly unblocked afterwards. This resulted in the last field
3076     being blocked for exactly one less than the number of frames of one player
3077     move. Additionally, even when blocking was disabled, the last field was
3078     blocked for exactly one frame.
3079     Since 3.1.1, due to changes in player movement handling, the last field
3080     is not blocked at all when blocking is disabled. When blocking is enabled,
3081     the last field is blocked for exactly the number of frames of one player
3082     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3083     last field is blocked for exactly one more than the number of frames of
3084     one player move.
3085
3086     Affected levels/tapes:
3087     (!!! yet to be determined -- probably many !!!)
3088   */
3089
3090   game.use_block_last_field_bug =
3091     (game.engine_version < VERSION_IDENT(3,1,1,0));
3092
3093   /* various special flags and settings for native Emerald Mine game engine */
3094
3095   game_em.use_single_button =
3096     (game.engine_version > VERSION_IDENT(4,0,0,2));
3097
3098   game_em.use_snap_key_bug =
3099     (game.engine_version < VERSION_IDENT(4,0,1,0));
3100
3101   game_em.use_random_bug =
3102     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3103
3104   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3105
3106   game_em.use_old_explosions            = use_old_em_engine;
3107   game_em.use_old_android               = use_old_em_engine;
3108   game_em.use_old_push_elements         = use_old_em_engine;
3109   game_em.use_old_push_into_acid        = use_old_em_engine;
3110
3111   game_em.use_wrap_around               = !use_old_em_engine;
3112
3113   // --------------------------------------------------------------------------
3114
3115   // set maximal allowed number of custom element changes per game frame
3116   game.max_num_changes_per_frame = 1;
3117
3118   // default scan direction: scan playfield from top/left to bottom/right
3119   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3120
3121   // dynamically adjust element properties according to game engine version
3122   InitElementPropertiesEngine(game.engine_version);
3123
3124   // ---------- initialize special element properties -------------------------
3125
3126   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3127   if (use_amoeba_dropping_cannot_fall_bug)
3128     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3129
3130   // ---------- initialize player's initial move delay ------------------------
3131
3132   // dynamically adjust player properties according to level information
3133   for (i = 0; i < MAX_PLAYERS; i++)
3134     game.initial_move_delay_value[i] =
3135       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3136
3137   // dynamically adjust player properties according to game engine version
3138   for (i = 0; i < MAX_PLAYERS; i++)
3139     game.initial_move_delay[i] =
3140       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3141        game.initial_move_delay_value[i] : 0);
3142
3143   // ---------- initialize player's initial push delay ------------------------
3144
3145   // dynamically adjust player properties according to game engine version
3146   game.initial_push_delay_value =
3147     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3148
3149   // ---------- initialize changing elements ----------------------------------
3150
3151   // initialize changing elements information
3152   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3153   {
3154     struct ElementInfo *ei = &element_info[i];
3155
3156     // this pointer might have been changed in the level editor
3157     ei->change = &ei->change_page[0];
3158
3159     if (!IS_CUSTOM_ELEMENT(i))
3160     {
3161       ei->change->target_element = EL_EMPTY_SPACE;
3162       ei->change->delay_fixed = 0;
3163       ei->change->delay_random = 0;
3164       ei->change->delay_frames = 1;
3165     }
3166
3167     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3168     {
3169       ei->has_change_event[j] = FALSE;
3170
3171       ei->event_page_nr[j] = 0;
3172       ei->event_page[j] = &ei->change_page[0];
3173     }
3174   }
3175
3176   // add changing elements from pre-defined list
3177   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3178   {
3179     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3180     struct ElementInfo *ei = &element_info[ch_delay->element];
3181
3182     ei->change->target_element       = ch_delay->target_element;
3183     ei->change->delay_fixed          = ch_delay->change_delay;
3184
3185     ei->change->pre_change_function  = ch_delay->pre_change_function;
3186     ei->change->change_function      = ch_delay->change_function;
3187     ei->change->post_change_function = ch_delay->post_change_function;
3188
3189     ei->change->can_change = TRUE;
3190     ei->change->can_change_or_has_action = TRUE;
3191
3192     ei->has_change_event[CE_DELAY] = TRUE;
3193
3194     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3195     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3196   }
3197
3198   // ---------- initialize if element can trigger global animations -----------
3199
3200   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3201   {
3202     struct ElementInfo *ei = &element_info[i];
3203
3204     ei->has_anim_event = FALSE;
3205   }
3206
3207   InitGlobalAnimEventsForCustomElements();
3208
3209   // ---------- initialize internal run-time variables ------------------------
3210
3211   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3212   {
3213     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3214
3215     for (j = 0; j < ei->num_change_pages; j++)
3216     {
3217       ei->change_page[j].can_change_or_has_action =
3218         (ei->change_page[j].can_change |
3219          ei->change_page[j].has_action);
3220     }
3221   }
3222
3223   // add change events from custom element configuration
3224   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3225   {
3226     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3227
3228     for (j = 0; j < ei->num_change_pages; j++)
3229     {
3230       if (!ei->change_page[j].can_change_or_has_action)
3231         continue;
3232
3233       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3234       {
3235         // only add event page for the first page found with this event
3236         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3237         {
3238           ei->has_change_event[k] = TRUE;
3239
3240           ei->event_page_nr[k] = j;
3241           ei->event_page[k] = &ei->change_page[j];
3242         }
3243       }
3244     }
3245   }
3246
3247   // ---------- initialize reference elements in change conditions ------------
3248
3249   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3250   {
3251     int element = EL_CUSTOM_START + i;
3252     struct ElementInfo *ei = &element_info[element];
3253
3254     for (j = 0; j < ei->num_change_pages; j++)
3255     {
3256       int trigger_element = ei->change_page[j].initial_trigger_element;
3257
3258       if (trigger_element >= EL_PREV_CE_8 &&
3259           trigger_element <= EL_NEXT_CE_8)
3260         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3261
3262       ei->change_page[j].trigger_element = trigger_element;
3263     }
3264   }
3265
3266   // ---------- initialize run-time trigger player and element ----------------
3267
3268   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3269   {
3270     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3271
3272     for (j = 0; j < ei->num_change_pages; j++)
3273     {
3274       struct ElementChangeInfo *change = &ei->change_page[j];
3275
3276       change->actual_trigger_element = EL_EMPTY;
3277       change->actual_trigger_player = EL_EMPTY;
3278       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3279       change->actual_trigger_side = CH_SIDE_NONE;
3280       change->actual_trigger_ce_value = 0;
3281       change->actual_trigger_ce_score = 0;
3282       change->actual_trigger_x = -1;
3283       change->actual_trigger_y = -1;
3284     }
3285   }
3286
3287   // ---------- initialize trigger events -------------------------------------
3288
3289   // initialize trigger events information
3290   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3291     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3292       trigger_events[i][j] = FALSE;
3293
3294   // add trigger events from element change event properties
3295   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3296   {
3297     struct ElementInfo *ei = &element_info[i];
3298
3299     for (j = 0; j < ei->num_change_pages; j++)
3300     {
3301       struct ElementChangeInfo *change = &ei->change_page[j];
3302
3303       if (!change->can_change_or_has_action)
3304         continue;
3305
3306       if (change->has_event[CE_BY_OTHER_ACTION])
3307       {
3308         int trigger_element = change->trigger_element;
3309
3310         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3311         {
3312           if (change->has_event[k])
3313           {
3314             if (IS_GROUP_ELEMENT(trigger_element))
3315             {
3316               struct ElementGroupInfo *group =
3317                 element_info[trigger_element].group;
3318
3319               for (l = 0; l < group->num_elements_resolved; l++)
3320                 trigger_events[group->element_resolved[l]][k] = TRUE;
3321             }
3322             else if (trigger_element == EL_ANY_ELEMENT)
3323               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3324                 trigger_events[l][k] = TRUE;
3325             else
3326               trigger_events[trigger_element][k] = TRUE;
3327           }
3328         }
3329       }
3330     }
3331   }
3332
3333   // ---------- initialize push delay -----------------------------------------
3334
3335   // initialize push delay values to default
3336   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3337   {
3338     if (!IS_CUSTOM_ELEMENT(i))
3339     {
3340       // set default push delay values (corrected since version 3.0.7-1)
3341       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3342       {
3343         element_info[i].push_delay_fixed = 2;
3344         element_info[i].push_delay_random = 8;
3345       }
3346       else
3347       {
3348         element_info[i].push_delay_fixed = 8;
3349         element_info[i].push_delay_random = 8;
3350       }
3351     }
3352   }
3353
3354   // set push delay value for certain elements from pre-defined list
3355   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3356   {
3357     int e = push_delay_list[i].element;
3358
3359     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3360     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3361   }
3362
3363   // set push delay value for Supaplex elements for newer engine versions
3364   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3365   {
3366     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3367     {
3368       if (IS_SP_ELEMENT(i))
3369       {
3370         // set SP push delay to just enough to push under a falling zonk
3371         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3372
3373         element_info[i].push_delay_fixed  = delay;
3374         element_info[i].push_delay_random = 0;
3375       }
3376     }
3377   }
3378
3379   // ---------- initialize move stepsize --------------------------------------
3380
3381   // initialize move stepsize values to default
3382   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3383     if (!IS_CUSTOM_ELEMENT(i))
3384       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3385
3386   // set move stepsize value for certain elements from pre-defined list
3387   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3388   {
3389     int e = move_stepsize_list[i].element;
3390
3391     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3392
3393     // set move stepsize value for certain elements for older engine versions
3394     if (use_old_move_stepsize_for_magic_wall)
3395     {
3396       if (e == EL_MAGIC_WALL_FILLING ||
3397           e == EL_MAGIC_WALL_EMPTYING ||
3398           e == EL_BD_MAGIC_WALL_FILLING ||
3399           e == EL_BD_MAGIC_WALL_EMPTYING)
3400         element_info[e].move_stepsize *= 2;
3401     }
3402   }
3403
3404   // ---------- initialize collect score --------------------------------------
3405
3406   // initialize collect score values for custom elements from initial value
3407   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3408     if (IS_CUSTOM_ELEMENT(i))
3409       element_info[i].collect_score = element_info[i].collect_score_initial;
3410
3411   // ---------- initialize collect count --------------------------------------
3412
3413   // initialize collect count values for non-custom elements
3414   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3415     if (!IS_CUSTOM_ELEMENT(i))
3416       element_info[i].collect_count_initial = 0;
3417
3418   // add collect count values for all elements from pre-defined list
3419   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3420     element_info[collect_count_list[i].element].collect_count_initial =
3421       collect_count_list[i].count;
3422
3423   // ---------- initialize access direction -----------------------------------
3424
3425   // initialize access direction values to default (access from every side)
3426   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3427     if (!IS_CUSTOM_ELEMENT(i))
3428       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3429
3430   // set access direction value for certain elements from pre-defined list
3431   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3432     element_info[access_direction_list[i].element].access_direction =
3433       access_direction_list[i].direction;
3434
3435   // ---------- initialize explosion content ----------------------------------
3436   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3437   {
3438     if (IS_CUSTOM_ELEMENT(i))
3439       continue;
3440
3441     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3442     {
3443       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3444
3445       element_info[i].content.e[x][y] =
3446         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3447          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3448          i == EL_PLAYER_3 ? EL_EMERALD :
3449          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3450          i == EL_MOLE ? EL_EMERALD_RED :
3451          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3452          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3453          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3454          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3455          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3456          i == EL_WALL_EMERALD ? EL_EMERALD :
3457          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3458          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3459          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3460          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3461          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3462          i == EL_WALL_PEARL ? EL_PEARL :
3463          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3464          EL_EMPTY);
3465     }
3466   }
3467
3468   // ---------- initialize recursion detection --------------------------------
3469   recursion_loop_depth = 0;
3470   recursion_loop_detected = FALSE;
3471   recursion_loop_element = EL_UNDEFINED;
3472
3473   // ---------- initialize graphics engine ------------------------------------
3474   game.scroll_delay_value =
3475     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3476      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3477      !setup.forced_scroll_delay           ? 0 :
3478      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3479   if (game.forced_scroll_delay_value == -1)
3480     game.scroll_delay_value =
3481       MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3482
3483   // ---------- initialize game engine snapshots ------------------------------
3484   for (i = 0; i < MAX_PLAYERS; i++)
3485     game.snapshot.last_action[i] = 0;
3486   game.snapshot.changed_action = FALSE;
3487   game.snapshot.collected_item = FALSE;
3488   game.snapshot.mode =
3489     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3490      SNAPSHOT_MODE_EVERY_STEP :
3491      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3492      SNAPSHOT_MODE_EVERY_MOVE :
3493      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3494      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3495   game.snapshot.save_snapshot = FALSE;
3496
3497   // ---------- initialize level time for Supaplex engine ---------------------
3498   // Supaplex levels with time limit currently unsupported -- should be added
3499   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3500     level.time = 0;
3501
3502   // ---------- initialize flags for handling game actions --------------------
3503
3504   // set flags for game actions to default values
3505   game.use_key_actions = TRUE;
3506   game.use_mouse_actions = FALSE;
3507
3508   // when using Mirror Magic game engine, handle mouse events only
3509   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3510   {
3511     game.use_key_actions = FALSE;
3512     game.use_mouse_actions = TRUE;
3513   }
3514
3515   // check for custom elements with mouse click events
3516   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3517   {
3518     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3519     {
3520       int element = EL_CUSTOM_START + i;
3521
3522       if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3523           HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3524           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3525           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3526         game.use_mouse_actions = TRUE;
3527     }
3528   }
3529 }
3530
3531 static int get_num_special_action(int element, int action_first,
3532                                   int action_last)
3533 {
3534   int num_special_action = 0;
3535   int i, j;
3536
3537   for (i = action_first; i <= action_last; i++)
3538   {
3539     boolean found = FALSE;
3540
3541     for (j = 0; j < NUM_DIRECTIONS; j++)
3542       if (el_act_dir2img(element, i, j) !=
3543           el_act_dir2img(element, ACTION_DEFAULT, j))
3544         found = TRUE;
3545
3546     if (found)
3547       num_special_action++;
3548     else
3549       break;
3550   }
3551
3552   return num_special_action;
3553 }
3554
3555
3556 // ============================================================================
3557 // InitGame()
3558 // ----------------------------------------------------------------------------
3559 // initialize and start new game
3560 // ============================================================================
3561
3562 #if DEBUG_INIT_PLAYER
3563 static void DebugPrintPlayerStatus(char *message)
3564 {
3565   int i;
3566
3567   if (!options.debug)
3568     return;
3569
3570   Debug("game:init:player", "%s:", message);
3571
3572   for (i = 0; i < MAX_PLAYERS; i++)
3573   {
3574     struct PlayerInfo *player = &stored_player[i];
3575
3576     Debug("game:init:player",
3577           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3578           i + 1,
3579           player->present,
3580           player->connected,
3581           player->connected_locally,
3582           player->connected_network,
3583           player->active,
3584           (local_player == player ? " (local player)" : ""));
3585   }
3586 }
3587 #endif
3588
3589 void InitGame(void)
3590 {
3591   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3592   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3593   int fade_mask = REDRAW_FIELD;
3594   boolean restarting = (game_status == GAME_MODE_PLAYING);
3595   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3596   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3597   int initial_move_dir = MV_DOWN;
3598   int i, j, x, y;
3599
3600   // required here to update video display before fading (FIX THIS)
3601   DrawMaskedBorder(REDRAW_DOOR_2);
3602
3603   if (!game.restart_level)
3604     CloseDoor(DOOR_CLOSE_1);
3605
3606   if (restarting)
3607   {
3608     // force fading out global animations displayed during game play
3609     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3610   }
3611   else
3612   {
3613     SetGameStatus(GAME_MODE_PLAYING);
3614   }
3615
3616   if (level_editor_test_game)
3617     FadeSkipNextFadeOut();
3618   else
3619     FadeSetEnterScreen();
3620
3621   if (CheckFadeAll())
3622     fade_mask = REDRAW_ALL;
3623
3624   FadeLevelSoundsAndMusic();
3625
3626   ExpireSoundLoops(TRUE);
3627
3628   FadeOut(fade_mask);
3629
3630   if (restarting)
3631   {
3632     // force restarting global animations displayed during game play
3633     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3634
3635     // this is required for "transforming" fade modes like cross-fading
3636     // (else global animations will be stopped, but not restarted here)
3637     SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING);
3638
3639     SetGameStatus(GAME_MODE_PLAYING);
3640   }
3641
3642   if (level_editor_test_game)
3643     FadeSkipNextFadeIn();
3644
3645   // needed if different viewport properties defined for playing
3646   ChangeViewportPropertiesIfNeeded();
3647
3648   ClearField();
3649
3650   DrawCompleteVideoDisplay();
3651
3652   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3653
3654   InitGameEngine();
3655   InitGameControlValues();
3656
3657   if (tape.recording)
3658   {
3659     // initialize tape actions from game when recording tape
3660     tape.use_key_actions   = game.use_key_actions;
3661     tape.use_mouse_actions = game.use_mouse_actions;
3662
3663     // initialize visible playfield size when recording tape (for team mode)
3664     tape.scr_fieldx = SCR_FIELDX;
3665     tape.scr_fieldy = SCR_FIELDY;
3666   }
3667
3668   // don't play tapes over network
3669   network_playing = (network.enabled && !tape.playing);
3670
3671   for (i = 0; i < MAX_PLAYERS; i++)
3672   {
3673     struct PlayerInfo *player = &stored_player[i];
3674
3675     player->index_nr = i;
3676     player->index_bit = (1 << i);
3677     player->element_nr = EL_PLAYER_1 + i;
3678
3679     player->present = FALSE;
3680     player->active = FALSE;
3681     player->mapped = FALSE;
3682
3683     player->killed = FALSE;
3684     player->reanimated = FALSE;
3685     player->buried = FALSE;
3686
3687     player->action = 0;
3688     player->effective_action = 0;
3689     player->programmed_action = 0;
3690     player->snap_action = 0;
3691
3692     player->mouse_action.lx = 0;
3693     player->mouse_action.ly = 0;
3694     player->mouse_action.button = 0;
3695     player->mouse_action.button_hint = 0;
3696
3697     player->effective_mouse_action.lx = 0;
3698     player->effective_mouse_action.ly = 0;
3699     player->effective_mouse_action.button = 0;
3700     player->effective_mouse_action.button_hint = 0;
3701
3702     for (j = 0; j < MAX_NUM_KEYS; j++)
3703       player->key[j] = FALSE;
3704
3705     player->num_white_keys = 0;
3706
3707     player->dynabomb_count = 0;
3708     player->dynabomb_size = 1;
3709     player->dynabombs_left = 0;
3710     player->dynabomb_xl = FALSE;
3711
3712     player->MovDir = initial_move_dir;
3713     player->MovPos = 0;
3714     player->GfxPos = 0;
3715     player->GfxDir = initial_move_dir;
3716     player->GfxAction = ACTION_DEFAULT;
3717     player->Frame = 0;
3718     player->StepFrame = 0;
3719
3720     player->initial_element = player->element_nr;
3721     player->artwork_element =
3722       (level.use_artwork_element[i] ? level.artwork_element[i] :
3723        player->element_nr);
3724     player->use_murphy = FALSE;
3725
3726     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3727     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3728
3729     player->gravity = level.initial_player_gravity[i];
3730
3731     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3732
3733     player->actual_frame_counter.count = 0;
3734     player->actual_frame_counter.value = 1;
3735
3736     player->step_counter = 0;
3737
3738     player->last_move_dir = initial_move_dir;
3739
3740     player->is_active = FALSE;
3741
3742     player->is_waiting = FALSE;
3743     player->is_moving = FALSE;
3744     player->is_auto_moving = FALSE;
3745     player->is_digging = FALSE;
3746     player->is_snapping = FALSE;
3747     player->is_collecting = FALSE;
3748     player->is_pushing = FALSE;
3749     player->is_switching = FALSE;
3750     player->is_dropping = FALSE;
3751     player->is_dropping_pressed = FALSE;
3752
3753     player->is_bored = FALSE;
3754     player->is_sleeping = FALSE;
3755
3756     player->was_waiting = TRUE;
3757     player->was_moving = FALSE;
3758     player->was_snapping = FALSE;
3759     player->was_dropping = FALSE;
3760
3761     player->force_dropping = FALSE;
3762
3763     player->frame_counter_bored = -1;
3764     player->frame_counter_sleeping = -1;
3765
3766     player->anim_delay_counter = 0;
3767     player->post_delay_counter = 0;
3768
3769     player->dir_waiting = initial_move_dir;
3770     player->action_waiting = ACTION_DEFAULT;
3771     player->last_action_waiting = ACTION_DEFAULT;
3772     player->special_action_bored = ACTION_DEFAULT;
3773     player->special_action_sleeping = ACTION_DEFAULT;
3774
3775     player->switch_x = -1;
3776     player->switch_y = -1;
3777
3778     player->drop_x = -1;
3779     player->drop_y = -1;
3780
3781     player->show_envelope = 0;
3782
3783     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3784
3785     player->push_delay       = -1;      // initialized when pushing starts
3786     player->push_delay_value = game.initial_push_delay_value;
3787
3788     player->drop_delay = 0;
3789     player->drop_pressed_delay = 0;
3790
3791     player->last_jx = -1;
3792     player->last_jy = -1;
3793     player->jx = -1;
3794     player->jy = -1;
3795
3796     player->shield_normal_time_left = 0;
3797     player->shield_deadly_time_left = 0;
3798
3799     player->last_removed_element = EL_UNDEFINED;
3800
3801     player->inventory_infinite_element = EL_UNDEFINED;
3802     player->inventory_size = 0;
3803
3804     if (level.use_initial_inventory[i])
3805     {
3806       for (j = 0; j < level.initial_inventory_size[i]; j++)
3807       {
3808         int element = level.initial_inventory_content[i][j];
3809         int collect_count = element_info[element].collect_count_initial;
3810         int k;
3811
3812         if (!IS_CUSTOM_ELEMENT(element))
3813           collect_count = 1;
3814
3815         if (collect_count == 0)
3816           player->inventory_infinite_element = element;
3817         else
3818           for (k = 0; k < collect_count; k++)
3819             if (player->inventory_size < MAX_INVENTORY_SIZE)
3820               player->inventory_element[player->inventory_size++] = element;
3821       }
3822     }
3823
3824     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3825     SnapField(player, 0, 0);
3826
3827     map_player_action[i] = i;
3828   }
3829
3830   network_player_action_received = FALSE;
3831
3832   // initial null action
3833   if (network_playing)
3834     SendToServer_MovePlayer(MV_NONE);
3835
3836   FrameCounter = 0;
3837   TimeFrames = 0;
3838   TimePlayed = 0;
3839   TimeLeft = level.time;
3840   TapeTime = 0;
3841
3842   ScreenMovDir = MV_NONE;
3843   ScreenMovPos = 0;
3844   ScreenGfxPos = 0;
3845
3846   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3847
3848   game.robot_wheel_x = -1;
3849   game.robot_wheel_y = -1;
3850
3851   game.exit_x = -1;
3852   game.exit_y = -1;
3853
3854   game.all_players_gone = FALSE;
3855
3856   game.LevelSolved = FALSE;
3857   game.GameOver = FALSE;
3858
3859   game.GamePlayed = !tape.playing;
3860
3861   game.LevelSolved_GameWon = FALSE;
3862   game.LevelSolved_GameEnd = FALSE;
3863   game.LevelSolved_SaveTape = FALSE;
3864   game.LevelSolved_SaveScore = FALSE;
3865
3866   game.LevelSolved_CountingTime = 0;
3867   game.LevelSolved_CountingScore = 0;
3868   game.LevelSolved_CountingHealth = 0;
3869
3870   game.panel.active = TRUE;
3871
3872   game.no_level_time_limit = (level.time == 0);
3873   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3874
3875   game.yamyam_content_nr = 0;
3876   game.robot_wheel_active = FALSE;
3877   game.magic_wall_active = FALSE;
3878   game.magic_wall_time_left = 0;
3879   game.light_time_left = 0;
3880   game.timegate_time_left = 0;
3881   game.switchgate_pos = 0;
3882   game.wind_direction = level.wind_direction_initial;
3883
3884   game.time_final = 0;
3885   game.score_time_final = 0;
3886
3887   game.score = 0;
3888   game.score_final = 0;
3889
3890   game.health = MAX_HEALTH;
3891   game.health_final = MAX_HEALTH;
3892
3893   game.gems_still_needed = level.gems_needed;
3894   game.sokoban_fields_still_needed = 0;
3895   game.sokoban_objects_still_needed = 0;
3896   game.lights_still_needed = 0;
3897   game.players_still_needed = 0;
3898   game.friends_still_needed = 0;
3899
3900   game.lenses_time_left = 0;
3901   game.magnify_time_left = 0;
3902
3903   game.ball_active = level.ball_active_initial;
3904   game.ball_content_nr = 0;
3905
3906   game.explosions_delayed = TRUE;
3907
3908   game.envelope_active = FALSE;
3909
3910   // special case: set custom artwork setting to initial value
3911   game.use_masked_elements = game.use_masked_elements_initial;
3912
3913   for (i = 0; i < NUM_BELTS; i++)
3914   {
3915     game.belt_dir[i] = MV_NONE;
3916     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3917   }
3918
3919   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3920     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3921
3922 #if DEBUG_INIT_PLAYER
3923   DebugPrintPlayerStatus("Player status at level initialization");
3924 #endif
3925
3926   SCAN_PLAYFIELD(x, y)
3927   {
3928     Tile[x][y] = Last[x][y] = level.field[x][y];
3929     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3930     ChangeDelay[x][y] = 0;
3931     ChangePage[x][y] = -1;
3932     CustomValue[x][y] = 0;              // initialized in InitField()
3933     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3934     AmoebaNr[x][y] = 0;
3935     WasJustMoving[x][y] = 0;
3936     WasJustFalling[x][y] = 0;
3937     CheckCollision[x][y] = 0;
3938     CheckImpact[x][y] = 0;
3939     Stop[x][y] = FALSE;
3940     Pushed[x][y] = FALSE;
3941
3942     ChangeCount[x][y] = 0;
3943     ChangeEvent[x][y] = -1;
3944
3945     ExplodePhase[x][y] = 0;
3946     ExplodeDelay[x][y] = 0;
3947     ExplodeField[x][y] = EX_TYPE_NONE;
3948
3949     RunnerVisit[x][y] = 0;
3950     PlayerVisit[x][y] = 0;
3951
3952     GfxFrame[x][y] = 0;
3953     GfxRandom[x][y] = INIT_GFX_RANDOM();
3954     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3955     GfxElement[x][y] = EL_UNDEFINED;
3956     GfxElementEmpty[x][y] = EL_EMPTY;
3957     GfxAction[x][y] = ACTION_DEFAULT;
3958     GfxDir[x][y] = MV_NONE;
3959     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3960   }
3961
3962   SCAN_PLAYFIELD(x, y)
3963   {
3964     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3965       emulate_bd = FALSE;
3966     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3967       emulate_sp = FALSE;
3968
3969     InitField(x, y, TRUE);
3970
3971     ResetGfxAnimation(x, y);
3972   }
3973
3974   InitBeltMovement();
3975
3976   for (i = 0; i < MAX_PLAYERS; i++)
3977   {
3978     struct PlayerInfo *player = &stored_player[i];
3979
3980     // set number of special actions for bored and sleeping animation
3981     player->num_special_action_bored =
3982       get_num_special_action(player->artwork_element,
3983                              ACTION_BORING_1, ACTION_BORING_LAST);
3984     player->num_special_action_sleeping =
3985       get_num_special_action(player->artwork_element,
3986                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3987   }
3988
3989   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3990                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3991
3992   // initialize type of slippery elements
3993   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3994   {
3995     if (!IS_CUSTOM_ELEMENT(i))
3996     {
3997       // default: elements slip down either to the left or right randomly
3998       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3999
4000       // SP style elements prefer to slip down on the left side
4001       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4002         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4003
4004       // BD style elements prefer to slip down on the left side
4005       if (game.emulation == EMU_BOULDERDASH)
4006         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4007     }
4008   }
4009
4010   // initialize explosion and ignition delay
4011   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4012   {
4013     if (!IS_CUSTOM_ELEMENT(i))
4014     {
4015       int num_phase = 8;
4016       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4017                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4018                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4019       int last_phase = (num_phase + 1) * delay;
4020       int half_phase = (num_phase / 2) * delay;
4021
4022       element_info[i].explosion_delay = last_phase - 1;
4023       element_info[i].ignition_delay = half_phase;
4024
4025       if (i == EL_BLACK_ORB)
4026         element_info[i].ignition_delay = 1;
4027     }
4028   }
4029
4030   // correct non-moving belts to start moving left
4031   for (i = 0; i < NUM_BELTS; i++)
4032     if (game.belt_dir[i] == MV_NONE)
4033       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4034
4035 #if USE_NEW_PLAYER_ASSIGNMENTS
4036   // use preferred player also in local single-player mode
4037   if (!network.enabled && !game.team_mode)
4038   {
4039     int new_index_nr = setup.network_player_nr;
4040
4041     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4042     {
4043       for (i = 0; i < MAX_PLAYERS; i++)
4044         stored_player[i].connected_locally = FALSE;
4045
4046       stored_player[new_index_nr].connected_locally = TRUE;
4047     }
4048   }
4049
4050   for (i = 0; i < MAX_PLAYERS; i++)
4051   {
4052     stored_player[i].connected = FALSE;
4053
4054     // in network game mode, the local player might not be the first player
4055     if (stored_player[i].connected_locally)
4056       local_player = &stored_player[i];
4057   }
4058
4059   if (!network.enabled)
4060     local_player->connected = TRUE;
4061
4062   if (tape.playing)
4063   {
4064     for (i = 0; i < MAX_PLAYERS; i++)
4065       stored_player[i].connected = tape.player_participates[i];
4066   }
4067   else if (network.enabled)
4068   {
4069     // add team mode players connected over the network (needed for correct
4070     // assignment of player figures from level to locally playing players)
4071
4072     for (i = 0; i < MAX_PLAYERS; i++)
4073       if (stored_player[i].connected_network)
4074         stored_player[i].connected = TRUE;
4075   }
4076   else if (game.team_mode)
4077   {
4078     // try to guess locally connected team mode players (needed for correct
4079     // assignment of player figures from level to locally playing players)
4080
4081     for (i = 0; i < MAX_PLAYERS; i++)
4082       if (setup.input[i].use_joystick ||
4083           setup.input[i].key.left != KSYM_UNDEFINED)
4084         stored_player[i].connected = TRUE;
4085   }
4086
4087 #if DEBUG_INIT_PLAYER
4088   DebugPrintPlayerStatus("Player status after level initialization");
4089 #endif
4090
4091 #if DEBUG_INIT_PLAYER
4092   Debug("game:init:player", "Reassigning players ...");
4093 #endif
4094
4095   // check if any connected player was not found in playfield
4096   for (i = 0; i < MAX_PLAYERS; i++)
4097   {
4098     struct PlayerInfo *player = &stored_player[i];
4099
4100     if (player->connected && !player->present)
4101     {
4102       struct PlayerInfo *field_player = NULL;
4103
4104 #if DEBUG_INIT_PLAYER
4105       Debug("game:init:player",
4106             "- looking for field player for player %d ...", i + 1);
4107 #endif
4108
4109       // assign first free player found that is present in the playfield
4110
4111       // first try: look for unmapped playfield player that is not connected
4112       for (j = 0; j < MAX_PLAYERS; j++)
4113         if (field_player == NULL &&
4114             stored_player[j].present &&
4115             !stored_player[j].mapped &&
4116             !stored_player[j].connected)
4117           field_player = &stored_player[j];
4118
4119       // second try: look for *any* unmapped playfield player
4120       for (j = 0; j < MAX_PLAYERS; j++)
4121         if (field_player == NULL &&
4122             stored_player[j].present &&
4123             !stored_player[j].mapped)
4124           field_player = &stored_player[j];
4125
4126       if (field_player != NULL)
4127       {
4128         int jx = field_player->jx, jy = field_player->jy;
4129
4130 #if DEBUG_INIT_PLAYER
4131         Debug("game:init:player", "- found player %d",
4132               field_player->index_nr + 1);
4133 #endif
4134
4135         player->present = FALSE;
4136         player->active = FALSE;
4137
4138         field_player->present = TRUE;
4139         field_player->active = TRUE;
4140
4141         /*
4142         player->initial_element = field_player->initial_element;
4143         player->artwork_element = field_player->artwork_element;
4144
4145         player->block_last_field       = field_player->block_last_field;
4146         player->block_delay_adjustment = field_player->block_delay_adjustment;
4147         */
4148
4149         StorePlayer[jx][jy] = field_player->element_nr;
4150
4151         field_player->jx = field_player->last_jx = jx;
4152         field_player->jy = field_player->last_jy = jy;
4153
4154         if (local_player == player)
4155           local_player = field_player;
4156
4157         map_player_action[field_player->index_nr] = i;
4158
4159         field_player->mapped = TRUE;
4160
4161 #if DEBUG_INIT_PLAYER
4162         Debug("game:init:player", "- map_player_action[%d] == %d",
4163               field_player->index_nr + 1, i + 1);
4164 #endif
4165       }
4166     }
4167
4168     if (player->connected && player->present)
4169       player->mapped = TRUE;
4170   }
4171
4172 #if DEBUG_INIT_PLAYER
4173   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4174 #endif
4175
4176 #else
4177
4178   // check if any connected player was not found in playfield
4179   for (i = 0; i < MAX_PLAYERS; i++)
4180   {
4181     struct PlayerInfo *player = &stored_player[i];
4182
4183     if (player->connected && !player->present)
4184     {
4185       for (j = 0; j < MAX_PLAYERS; j++)
4186       {
4187         struct PlayerInfo *field_player = &stored_player[j];
4188         int jx = field_player->jx, jy = field_player->jy;
4189
4190         // assign first free player found that is present in the playfield
4191         if (field_player->present && !field_player->connected)
4192         {
4193           player->present = TRUE;
4194           player->active = TRUE;
4195
4196           field_player->present = FALSE;
4197           field_player->active = FALSE;
4198
4199           player->initial_element = field_player->initial_element;
4200           player->artwork_element = field_player->artwork_element;
4201
4202           player->block_last_field       = field_player->block_last_field;
4203           player->block_delay_adjustment = field_player->block_delay_adjustment;
4204
4205           StorePlayer[jx][jy] = player->element_nr;
4206
4207           player->jx = player->last_jx = jx;
4208           player->jy = player->last_jy = jy;
4209
4210           break;
4211         }
4212       }
4213     }
4214   }
4215 #endif
4216
4217 #if 0
4218   Debug("game:init:player", "local_player->present == %d",
4219         local_player->present);
4220 #endif
4221
4222   // set focus to local player for network games, else to all players
4223   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4224   game.centered_player_nr_next = game.centered_player_nr;
4225   game.set_centered_player = FALSE;
4226   game.set_centered_player_wrap = FALSE;
4227
4228   if (network_playing && tape.recording)
4229   {
4230     // store client dependent player focus when recording network games
4231     tape.centered_player_nr_next = game.centered_player_nr_next;
4232     tape.set_centered_player = TRUE;
4233   }
4234
4235   if (tape.playing)
4236   {
4237     // when playing a tape, eliminate all players who do not participate
4238
4239 #if USE_NEW_PLAYER_ASSIGNMENTS
4240
4241     if (!game.team_mode)
4242     {
4243       for (i = 0; i < MAX_PLAYERS; i++)
4244       {
4245         if (stored_player[i].active &&
4246             !tape.player_participates[map_player_action[i]])
4247         {
4248           struct PlayerInfo *player = &stored_player[i];
4249           int jx = player->jx, jy = player->jy;
4250
4251 #if DEBUG_INIT_PLAYER
4252           Debug("game:init:player", "Removing player %d at (%d, %d)",
4253                 i + 1, jx, jy);
4254 #endif
4255
4256           player->active = FALSE;
4257           StorePlayer[jx][jy] = 0;
4258           Tile[jx][jy] = EL_EMPTY;
4259         }
4260       }
4261     }
4262
4263 #else
4264
4265     for (i = 0; i < MAX_PLAYERS; i++)
4266     {
4267       if (stored_player[i].active &&
4268           !tape.player_participates[i])
4269       {
4270         struct PlayerInfo *player = &stored_player[i];
4271         int jx = player->jx, jy = player->jy;
4272
4273         player->active = FALSE;
4274         StorePlayer[jx][jy] = 0;
4275         Tile[jx][jy] = EL_EMPTY;
4276       }
4277     }
4278 #endif
4279   }
4280   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4281   {
4282     // when in single player mode, eliminate all but the local player
4283
4284     for (i = 0; i < MAX_PLAYERS; i++)
4285     {
4286       struct PlayerInfo *player = &stored_player[i];
4287
4288       if (player->active && player != local_player)
4289       {
4290         int jx = player->jx, jy = player->jy;
4291
4292         player->active = FALSE;
4293         player->present = FALSE;
4294
4295         StorePlayer[jx][jy] = 0;
4296         Tile[jx][jy] = EL_EMPTY;
4297       }
4298     }
4299   }
4300
4301   for (i = 0; i < MAX_PLAYERS; i++)
4302     if (stored_player[i].active)
4303       game.players_still_needed++;
4304
4305   if (level.solved_by_one_player)
4306     game.players_still_needed = 1;
4307
4308   // when recording the game, store which players take part in the game
4309   if (tape.recording)
4310   {
4311 #if USE_NEW_PLAYER_ASSIGNMENTS
4312     for (i = 0; i < MAX_PLAYERS; i++)
4313       if (stored_player[i].connected)
4314         tape.player_participates[i] = TRUE;
4315 #else
4316     for (i = 0; i < MAX_PLAYERS; i++)
4317       if (stored_player[i].active)
4318         tape.player_participates[i] = TRUE;
4319 #endif
4320   }
4321
4322 #if DEBUG_INIT_PLAYER
4323   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4324 #endif
4325
4326   if (BorderElement == EL_EMPTY)
4327   {
4328     SBX_Left = 0;
4329     SBX_Right = lev_fieldx - SCR_FIELDX;
4330     SBY_Upper = 0;
4331     SBY_Lower = lev_fieldy - SCR_FIELDY;
4332   }
4333   else
4334   {
4335     SBX_Left = -1;
4336     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4337     SBY_Upper = -1;
4338     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4339   }
4340
4341   if (full_lev_fieldx <= SCR_FIELDX)
4342     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4343   if (full_lev_fieldy <= SCR_FIELDY)
4344     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4345
4346   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4347     SBX_Left--;
4348   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4349     SBY_Upper--;
4350
4351   // if local player not found, look for custom element that might create
4352   // the player (make some assumptions about the right custom element)
4353   if (!local_player->present)
4354   {
4355     int start_x = 0, start_y = 0;
4356     int found_rating = 0;
4357     int found_element = EL_UNDEFINED;
4358     int player_nr = local_player->index_nr;
4359
4360     SCAN_PLAYFIELD(x, y)
4361     {
4362       int element = Tile[x][y];
4363       int content;
4364       int xx, yy;
4365       boolean is_player;
4366
4367       if (level.use_start_element[player_nr] &&
4368           level.start_element[player_nr] == element &&
4369           found_rating < 4)
4370       {
4371         start_x = x;
4372         start_y = y;
4373
4374         found_rating = 4;
4375         found_element = element;
4376       }
4377
4378       if (!IS_CUSTOM_ELEMENT(element))
4379         continue;
4380
4381       if (CAN_CHANGE(element))
4382       {
4383         for (i = 0; i < element_info[element].num_change_pages; i++)
4384         {
4385           // check for player created from custom element as single target
4386           content = element_info[element].change_page[i].target_element;
4387           is_player = IS_PLAYER_ELEMENT(content);
4388
4389           if (is_player && (found_rating < 3 ||
4390                             (found_rating == 3 && element < found_element)))
4391           {
4392             start_x = x;
4393             start_y = y;
4394
4395             found_rating = 3;
4396             found_element = element;
4397           }
4398         }
4399       }
4400
4401       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4402       {
4403         // check for player created from custom element as explosion content
4404         content = element_info[element].content.e[xx][yy];
4405         is_player = IS_PLAYER_ELEMENT(content);
4406
4407         if (is_player && (found_rating < 2 ||
4408                           (found_rating == 2 && element < found_element)))
4409         {
4410           start_x = x + xx - 1;
4411           start_y = y + yy - 1;
4412
4413           found_rating = 2;
4414           found_element = element;
4415         }
4416
4417         if (!CAN_CHANGE(element))
4418           continue;
4419
4420         for (i = 0; i < element_info[element].num_change_pages; i++)
4421         {
4422           // check for player created from custom element as extended target
4423           content =
4424             element_info[element].change_page[i].target_content.e[xx][yy];
4425
4426           is_player = IS_PLAYER_ELEMENT(content);
4427
4428           if (is_player && (found_rating < 1 ||
4429                             (found_rating == 1 && element < found_element)))
4430           {
4431             start_x = x + xx - 1;
4432             start_y = y + yy - 1;
4433
4434             found_rating = 1;
4435             found_element = element;
4436           }
4437         }
4438       }
4439     }
4440
4441     scroll_x = SCROLL_POSITION_X(start_x);
4442     scroll_y = SCROLL_POSITION_Y(start_y);
4443   }
4444   else
4445   {
4446     scroll_x = SCROLL_POSITION_X(local_player->jx);
4447     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4448   }
4449
4450   if (game.forced_scroll_x != ARG_UNDEFINED_VALUE)
4451     scroll_x = game.forced_scroll_x;
4452   if (game.forced_scroll_y != ARG_UNDEFINED_VALUE)
4453     scroll_y = game.forced_scroll_y;
4454
4455   // !!! FIX THIS (START) !!!
4456   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4457   {
4458     InitGameEngine_EM();
4459   }
4460   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4461   {
4462     InitGameEngine_SP();
4463   }
4464   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4465   {
4466     InitGameEngine_MM();
4467   }
4468   else
4469   {
4470     DrawLevel(REDRAW_FIELD);
4471     DrawAllPlayers();
4472
4473     // after drawing the level, correct some elements
4474     if (game.timegate_time_left == 0)
4475       CloseAllOpenTimegates();
4476   }
4477
4478   // blit playfield from scroll buffer to normal back buffer for fading in
4479   BlitScreenToBitmap(backbuffer);
4480   // !!! FIX THIS (END) !!!
4481
4482   DrawMaskedBorder(fade_mask);
4483
4484   FadeIn(fade_mask);
4485
4486 #if 1
4487   // full screen redraw is required at this point in the following cases:
4488   // - special editor door undrawn when game was started from level editor
4489   // - drawing area (playfield) was changed and has to be removed completely
4490   redraw_mask = REDRAW_ALL;
4491   BackToFront();
4492 #endif
4493
4494   if (!game.restart_level)
4495   {
4496     // copy default game door content to main double buffer
4497
4498     // !!! CHECK AGAIN !!!
4499     SetPanelBackground();
4500     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4501     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4502   }
4503
4504   SetPanelBackground();
4505   SetDrawBackgroundMask(REDRAW_DOOR_1);
4506
4507   UpdateAndDisplayGameControlValues();
4508
4509   if (!game.restart_level)
4510   {
4511     UnmapGameButtons();
4512     UnmapTapeButtons();
4513
4514     FreeGameButtons();
4515     CreateGameButtons();
4516
4517     MapGameButtons();
4518     MapTapeButtons();
4519
4520     // copy actual game door content to door double buffer for OpenDoor()
4521     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4522
4523     OpenDoor(DOOR_OPEN_ALL);
4524
4525     KeyboardAutoRepeatOffUnlessAutoplay();
4526
4527 #if DEBUG_INIT_PLAYER
4528     DebugPrintPlayerStatus("Player status (final)");
4529 #endif
4530   }
4531
4532   UnmapAllGadgets();
4533
4534   MapGameButtons();
4535   MapTapeButtons();
4536
4537   if (!game.restart_level && !tape.playing)
4538   {
4539     LevelStats_incPlayed(level_nr);
4540
4541     SaveLevelSetup_SeriesInfo();
4542   }
4543
4544   game.restart_level = FALSE;
4545
4546   game.request_active = FALSE;
4547   game.request_active_or_moving = FALSE;
4548
4549   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4550     InitGameActions_MM();
4551
4552   SaveEngineSnapshotToListInitial();
4553
4554   if (!game.restart_level)
4555   {
4556     PlaySound(SND_GAME_STARTING);
4557
4558     if (setup.sound_music)
4559       PlayLevelMusic();
4560   }
4561
4562   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4563 }
4564
4565 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4566                         int actual_player_x, int actual_player_y)
4567 {
4568   // this is used for non-R'n'D game engines to update certain engine values
4569
4570   // needed to determine if sounds are played within the visible screen area
4571   scroll_x = actual_scroll_x;
4572   scroll_y = actual_scroll_y;
4573
4574   // needed to get player position for "follow finger" playing input method
4575   local_player->jx = actual_player_x;
4576   local_player->jy = actual_player_y;
4577 }
4578
4579 void InitMovDir(int x, int y)
4580 {
4581   int i, element = Tile[x][y];
4582   static int xy[4][2] =
4583   {
4584     {  0, +1 },
4585     { +1,  0 },
4586     {  0, -1 },
4587     { -1,  0 }
4588   };
4589   static int direction[3][4] =
4590   {
4591     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4592     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4593     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4594   };
4595
4596   switch (element)
4597   {
4598     case EL_BUG_RIGHT:
4599     case EL_BUG_UP:
4600     case EL_BUG_LEFT:
4601     case EL_BUG_DOWN:
4602       Tile[x][y] = EL_BUG;
4603       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4604       break;
4605
4606     case EL_SPACESHIP_RIGHT:
4607     case EL_SPACESHIP_UP:
4608     case EL_SPACESHIP_LEFT:
4609     case EL_SPACESHIP_DOWN:
4610       Tile[x][y] = EL_SPACESHIP;
4611       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4612       break;
4613
4614     case EL_BD_BUTTERFLY_RIGHT:
4615     case EL_BD_BUTTERFLY_UP:
4616     case EL_BD_BUTTERFLY_LEFT:
4617     case EL_BD_BUTTERFLY_DOWN:
4618       Tile[x][y] = EL_BD_BUTTERFLY;
4619       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4620       break;
4621
4622     case EL_BD_FIREFLY_RIGHT:
4623     case EL_BD_FIREFLY_UP:
4624     case EL_BD_FIREFLY_LEFT:
4625     case EL_BD_FIREFLY_DOWN:
4626       Tile[x][y] = EL_BD_FIREFLY;
4627       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4628       break;
4629
4630     case EL_PACMAN_RIGHT:
4631     case EL_PACMAN_UP:
4632     case EL_PACMAN_LEFT:
4633     case EL_PACMAN_DOWN:
4634       Tile[x][y] = EL_PACMAN;
4635       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4636       break;
4637
4638     case EL_YAMYAM_LEFT:
4639     case EL_YAMYAM_RIGHT:
4640     case EL_YAMYAM_UP:
4641     case EL_YAMYAM_DOWN:
4642       Tile[x][y] = EL_YAMYAM;
4643       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4644       break;
4645
4646     case EL_SP_SNIKSNAK:
4647       MovDir[x][y] = MV_UP;
4648       break;
4649
4650     case EL_SP_ELECTRON:
4651       MovDir[x][y] = MV_LEFT;
4652       break;
4653
4654     case EL_MOLE_LEFT:
4655     case EL_MOLE_RIGHT:
4656     case EL_MOLE_UP:
4657     case EL_MOLE_DOWN:
4658       Tile[x][y] = EL_MOLE;
4659       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4660       break;
4661
4662     case EL_SPRING_LEFT:
4663     case EL_SPRING_RIGHT:
4664       Tile[x][y] = EL_SPRING;
4665       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4666       break;
4667
4668     default:
4669       if (IS_CUSTOM_ELEMENT(element))
4670       {
4671         struct ElementInfo *ei = &element_info[element];
4672         int move_direction_initial = ei->move_direction_initial;
4673         int move_pattern = ei->move_pattern;
4674
4675         if (move_direction_initial == MV_START_PREVIOUS)
4676         {
4677           if (MovDir[x][y] != MV_NONE)
4678             return;
4679
4680           move_direction_initial = MV_START_AUTOMATIC;
4681         }
4682
4683         if (move_direction_initial == MV_START_RANDOM)
4684           MovDir[x][y] = 1 << RND(4);
4685         else if (move_direction_initial & MV_ANY_DIRECTION)
4686           MovDir[x][y] = move_direction_initial;
4687         else if (move_pattern == MV_ALL_DIRECTIONS ||
4688                  move_pattern == MV_TURNING_LEFT ||
4689                  move_pattern == MV_TURNING_RIGHT ||
4690                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4691                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4692                  move_pattern == MV_TURNING_RANDOM)
4693           MovDir[x][y] = 1 << RND(4);
4694         else if (move_pattern == MV_HORIZONTAL)
4695           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4696         else if (move_pattern == MV_VERTICAL)
4697           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4698         else if (move_pattern & MV_ANY_DIRECTION)
4699           MovDir[x][y] = element_info[element].move_pattern;
4700         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4701                  move_pattern == MV_ALONG_RIGHT_SIDE)
4702         {
4703           // use random direction as default start direction
4704           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4705             MovDir[x][y] = 1 << RND(4);
4706
4707           for (i = 0; i < NUM_DIRECTIONS; i++)
4708           {
4709             int x1 = x + xy[i][0];
4710             int y1 = y + xy[i][1];
4711
4712             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4713             {
4714               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4715                 MovDir[x][y] = direction[0][i];
4716               else
4717                 MovDir[x][y] = direction[1][i];
4718
4719               break;
4720             }
4721           }
4722         }                
4723       }
4724       else
4725       {
4726         MovDir[x][y] = 1 << RND(4);
4727
4728         if (element != EL_BUG &&
4729             element != EL_SPACESHIP &&
4730             element != EL_BD_BUTTERFLY &&
4731             element != EL_BD_FIREFLY)
4732           break;
4733
4734         for (i = 0; i < NUM_DIRECTIONS; i++)
4735         {
4736           int x1 = x + xy[i][0];
4737           int y1 = y + xy[i][1];
4738
4739           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4740           {
4741             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4742             {
4743               MovDir[x][y] = direction[0][i];
4744               break;
4745             }
4746             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4747                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4748             {
4749               MovDir[x][y] = direction[1][i];
4750               break;
4751             }
4752           }
4753         }
4754       }
4755       break;
4756   }
4757
4758   GfxDir[x][y] = MovDir[x][y];
4759 }
4760
4761 void InitAmoebaNr(int x, int y)
4762 {
4763   int i;
4764   int group_nr = AmoebaNeighbourNr(x, y);
4765
4766   if (group_nr == 0)
4767   {
4768     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4769     {
4770       if (AmoebaCnt[i] == 0)
4771       {
4772         group_nr = i;
4773         break;
4774       }
4775     }
4776   }
4777
4778   AmoebaNr[x][y] = group_nr;
4779   AmoebaCnt[group_nr]++;
4780   AmoebaCnt2[group_nr]++;
4781 }
4782
4783 static void LevelSolved_SetFinalGameValues(void)
4784 {
4785   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4786   game.score_time_final = (level.use_step_counter ? TimePlayed :
4787                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4788
4789   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4790                       game_em.lev->score :
4791                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4792                       game_mm.score :
4793                       game.score);
4794
4795   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4796                        MM_HEALTH(game_mm.laser_overload_value) :
4797                        game.health);
4798
4799   game.LevelSolved_CountingTime = game.time_final;
4800   game.LevelSolved_CountingScore = game.score_final;
4801   game.LevelSolved_CountingHealth = game.health_final;
4802 }
4803
4804 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4805 {
4806   game.LevelSolved_CountingTime = time;
4807   game.LevelSolved_CountingScore = score;
4808   game.LevelSolved_CountingHealth = health;
4809
4810   game_panel_controls[GAME_PANEL_TIME].value = time;
4811   game_panel_controls[GAME_PANEL_SCORE].value = score;
4812   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4813
4814   DisplayGameControlValues();
4815 }
4816
4817 static void LevelSolved(void)
4818 {
4819   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4820       game.players_still_needed > 0)
4821     return;
4822
4823   game.LevelSolved = TRUE;
4824   game.GameOver = TRUE;
4825
4826   tape.solved = TRUE;
4827
4828   // needed here to display correct panel values while player walks into exit
4829   LevelSolved_SetFinalGameValues();
4830 }
4831
4832 void GameWon(void)
4833 {
4834   static int time_count_steps;
4835   static int time, time_final;
4836   static float score, score_final; // needed for time score < 10 for 10 seconds
4837   static int health, health_final;
4838   static int game_over_delay_1 = 0;
4839   static int game_over_delay_2 = 0;
4840   static int game_over_delay_3 = 0;
4841   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4842   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4843
4844   if (!game.LevelSolved_GameWon)
4845   {
4846     int i;
4847
4848     // do not start end game actions before the player stops moving (to exit)
4849     if (local_player->active && local_player->MovPos)
4850       return;
4851
4852     // calculate final game values after player finished walking into exit
4853     LevelSolved_SetFinalGameValues();
4854
4855     game.LevelSolved_GameWon = TRUE;
4856     game.LevelSolved_SaveTape = tape.recording;
4857     game.LevelSolved_SaveScore = !tape.playing;
4858
4859     if (!tape.playing)
4860     {
4861       LevelStats_incSolved(level_nr);
4862
4863       SaveLevelSetup_SeriesInfo();
4864     }
4865
4866     if (tape.auto_play)         // tape might already be stopped here
4867       tape.auto_play_level_solved = TRUE;
4868
4869     TapeStop();
4870
4871     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4872     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4873     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4874
4875     time = time_final = game.time_final;
4876     score = score_final = game.score_final;
4877     health = health_final = game.health_final;
4878
4879     // update game panel values before (delayed) counting of score (if any)
4880     LevelSolved_DisplayFinalGameValues(time, score, health);
4881
4882     // if level has time score defined, calculate new final game values
4883     if (time_score > 0)
4884     {
4885       int time_final_max = 999;
4886       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4887       int time_frames = 0;
4888       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4889       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4890
4891       if (TimeLeft > 0)
4892       {
4893         time_final = 0;
4894         time_frames = time_frames_left;
4895       }
4896       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4897       {
4898         time_final = time_final_max;
4899         time_frames = time_frames_final_max - time_frames_played;
4900       }
4901
4902       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4903
4904       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4905
4906       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4907       {
4908         health_final = 0;
4909         score_final += health * time_score;
4910       }
4911
4912       game.score_final = score_final;
4913       game.health_final = health_final;
4914     }
4915
4916     // if not counting score after game, immediately update game panel values
4917     if (level_editor_test_game || !setup.count_score_after_game)
4918     {
4919       time = time_final;
4920       score = score_final;
4921
4922       LevelSolved_DisplayFinalGameValues(time, score, health);
4923     }
4924
4925     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4926     {
4927       // check if last player has left the level
4928       if (game.exit_x >= 0 &&
4929           game.exit_y >= 0)
4930       {
4931         int x = game.exit_x;
4932         int y = game.exit_y;
4933         int element = Tile[x][y];
4934
4935         // close exit door after last player
4936         if ((game.all_players_gone &&
4937              (element == EL_EXIT_OPEN ||
4938               element == EL_SP_EXIT_OPEN ||
4939               element == EL_STEEL_EXIT_OPEN)) ||
4940             element == EL_EM_EXIT_OPEN ||
4941             element == EL_EM_STEEL_EXIT_OPEN)
4942         {
4943
4944           Tile[x][y] =
4945             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4946              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4947              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4948              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4949              EL_EM_STEEL_EXIT_CLOSING);
4950
4951           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4952         }
4953
4954         // player disappears
4955         DrawLevelField(x, y);
4956       }
4957
4958       for (i = 0; i < MAX_PLAYERS; i++)
4959       {
4960         struct PlayerInfo *player = &stored_player[i];
4961
4962         if (player->present)
4963         {
4964           RemovePlayer(player);
4965
4966           // player disappears
4967           DrawLevelField(player->jx, player->jy);
4968         }
4969       }
4970     }
4971
4972     PlaySound(SND_GAME_WINNING);
4973   }
4974
4975   if (setup.count_score_after_game)
4976   {
4977     if (time != time_final)
4978     {
4979       if (game_over_delay_1 > 0)
4980       {
4981         game_over_delay_1--;
4982
4983         return;
4984       }
4985
4986       int time_to_go = ABS(time_final - time);
4987       int time_count_dir = (time < time_final ? +1 : -1);
4988
4989       if (time_to_go < time_count_steps)
4990         time_count_steps = 1;
4991
4992       time  += time_count_steps * time_count_dir;
4993       score += time_count_steps * time_score;
4994
4995       // set final score to correct rounding differences after counting score
4996       if (time == time_final)
4997         score = score_final;
4998
4999       LevelSolved_DisplayFinalGameValues(time, score, health);
5000
5001       if (time == time_final)
5002         StopSound(SND_GAME_LEVELTIME_BONUS);
5003       else if (setup.sound_loops)
5004         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5005       else
5006         PlaySound(SND_GAME_LEVELTIME_BONUS);
5007
5008       return;
5009     }
5010
5011     if (health != health_final)
5012     {
5013       if (game_over_delay_2 > 0)
5014       {
5015         game_over_delay_2--;
5016
5017         return;
5018       }
5019
5020       int health_count_dir = (health < health_final ? +1 : -1);
5021
5022       health += health_count_dir;
5023       score  += time_score;
5024
5025       LevelSolved_DisplayFinalGameValues(time, score, health);
5026
5027       if (health == health_final)
5028         StopSound(SND_GAME_LEVELTIME_BONUS);
5029       else if (setup.sound_loops)
5030         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5031       else
5032         PlaySound(SND_GAME_LEVELTIME_BONUS);
5033
5034       return;
5035     }
5036   }
5037
5038   game.panel.active = FALSE;
5039
5040   if (game_over_delay_3 > 0)
5041   {
5042     game_over_delay_3--;
5043
5044     return;
5045   }
5046
5047   GameEnd();
5048 }
5049
5050 void GameEnd(void)
5051 {
5052   // used instead of "level_nr" (needed for network games)
5053   int last_level_nr = levelset.level_nr;
5054   boolean tape_saved = FALSE;
5055
5056   game.LevelSolved_GameEnd = TRUE;
5057
5058   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5059   {
5060     // make sure that request dialog to save tape does not open door again
5061     if (!global.use_envelope_request)
5062       CloseDoor(DOOR_CLOSE_1);
5063
5064     // ask to save tape
5065     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5066
5067     // set unique basename for score tape (also saved in high score table)
5068     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5069   }
5070
5071   // if no tape is to be saved, close both doors simultaneously
5072   CloseDoor(DOOR_CLOSE_ALL);
5073
5074   if (level_editor_test_game || score_info_tape_play)
5075   {
5076     SetGameStatus(GAME_MODE_MAIN);
5077
5078     DrawMainMenu();
5079
5080     return;
5081   }
5082
5083   if (!game.LevelSolved_SaveScore)
5084   {
5085     SetGameStatus(GAME_MODE_MAIN);
5086
5087     DrawMainMenu();
5088
5089     return;
5090   }
5091
5092   if (level_nr == leveldir_current->handicap_level)
5093   {
5094     leveldir_current->handicap_level++;
5095
5096     SaveLevelSetup_SeriesInfo();
5097   }
5098
5099   // save score and score tape before potentially erasing tape below
5100   NewHighScore(last_level_nr, tape_saved);
5101
5102   if (setup.increment_levels &&
5103       level_nr < leveldir_current->last_level &&
5104       !network_playing)
5105   {
5106     level_nr++;         // advance to next level
5107     TapeErase();        // start with empty tape
5108
5109     if (setup.auto_play_next_level)
5110     {
5111       scores.continue_playing = TRUE;
5112       scores.next_level_nr = level_nr;
5113
5114       LoadLevel(level_nr);
5115
5116       SaveLevelSetup_SeriesInfo();
5117     }
5118   }
5119
5120   if (scores.last_added >= 0 && setup.show_scores_after_game)
5121   {
5122     SetGameStatus(GAME_MODE_SCORES);
5123
5124     DrawHallOfFame(last_level_nr);
5125   }
5126   else if (scores.continue_playing)
5127   {
5128     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5129   }
5130   else
5131   {
5132     SetGameStatus(GAME_MODE_MAIN);
5133
5134     DrawMainMenu();
5135   }
5136 }
5137
5138 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5139                          boolean one_score_entry_per_name)
5140 {
5141   int i;
5142
5143   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5144     return -1;
5145
5146   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5147   {
5148     struct ScoreEntry *entry = &list->entry[i];
5149     boolean score_is_better = (new_entry->score >  entry->score);
5150     boolean score_is_equal  = (new_entry->score == entry->score);
5151     boolean time_is_better  = (new_entry->time  <  entry->time);
5152     boolean time_is_equal   = (new_entry->time  == entry->time);
5153     boolean better_by_score = (score_is_better ||
5154                                (score_is_equal && time_is_better));
5155     boolean better_by_time  = (time_is_better ||
5156                                (time_is_equal && score_is_better));
5157     boolean is_better = (level.rate_time_over_score ? better_by_time :
5158                          better_by_score);
5159     boolean entry_is_empty = (entry->score == 0 &&
5160                               entry->time == 0);
5161
5162     // prevent adding server score entries if also existing in local score file
5163     // (special case: historic score entries have an empty tape basename entry)
5164     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5165         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5166     {
5167       // add fields from server score entry not stored in local score entry
5168       // (currently, this means setting platform, version and country fields;
5169       // in rare cases, this may also correct an invalid score value, as
5170       // historic scores might have been truncated to 16-bit values locally)
5171       *entry = *new_entry;
5172
5173       return -1;
5174     }
5175
5176     if (is_better || entry_is_empty)
5177     {
5178       // player has made it to the hall of fame
5179
5180       if (i < MAX_SCORE_ENTRIES - 1)
5181       {
5182         int m = MAX_SCORE_ENTRIES - 1;
5183         int l;
5184
5185         if (one_score_entry_per_name)
5186         {
5187           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5188             if (strEqual(list->entry[l].name, new_entry->name))
5189               m = l;
5190
5191           if (m == i)   // player's new highscore overwrites his old one
5192             goto put_into_list;
5193         }
5194
5195         for (l = m; l > i; l--)
5196           list->entry[l] = list->entry[l - 1];
5197       }
5198
5199       put_into_list:
5200
5201       *entry = *new_entry;
5202
5203       return i;
5204     }
5205     else if (one_score_entry_per_name &&
5206              strEqual(entry->name, new_entry->name))
5207     {
5208       // player already in high score list with better score or time
5209
5210       return -1;
5211     }
5212   }
5213
5214   // special case: new score is beyond the last high score list position
5215   return MAX_SCORE_ENTRIES;
5216 }
5217
5218 void NewHighScore(int level_nr, boolean tape_saved)
5219 {
5220   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5221   boolean one_per_name = FALSE;
5222
5223   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5224   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5225
5226   new_entry.score = game.score_final;
5227   new_entry.time = game.score_time_final;
5228
5229   LoadScore(level_nr);
5230
5231   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5232
5233   if (scores.last_added >= MAX_SCORE_ENTRIES)
5234   {
5235     scores.last_added = MAX_SCORE_ENTRIES - 1;
5236     scores.force_last_added = TRUE;
5237
5238     scores.entry[scores.last_added] = new_entry;
5239
5240     // store last added local score entry (before merging server scores)
5241     scores.last_added_local = scores.last_added;
5242
5243     return;
5244   }
5245
5246   if (scores.last_added < 0)
5247     return;
5248
5249   SaveScore(level_nr);
5250
5251   // store last added local score entry (before merging server scores)
5252   scores.last_added_local = scores.last_added;
5253
5254   if (!game.LevelSolved_SaveTape)
5255     return;
5256
5257   SaveScoreTape(level_nr);
5258
5259   if (setup.ask_for_using_api_server)
5260   {
5261     setup.use_api_server =
5262       Request("Upload your score and tape to the high score server?", REQ_ASK);
5263
5264     if (!setup.use_api_server)
5265       Request("Not using high score server! Use setup menu to enable again!",
5266               REQ_CONFIRM);
5267
5268     runtime.use_api_server = setup.use_api_server;
5269
5270     // after asking for using API server once, do not ask again
5271     setup.ask_for_using_api_server = FALSE;
5272
5273     SaveSetup_ServerSetup();
5274   }
5275
5276   SaveServerScore(level_nr, tape_saved);
5277 }
5278
5279 void MergeServerScore(void)
5280 {
5281   struct ScoreEntry last_added_entry;
5282   boolean one_per_name = FALSE;
5283   int i;
5284
5285   if (scores.last_added >= 0)
5286     last_added_entry = scores.entry[scores.last_added];
5287
5288   for (i = 0; i < server_scores.num_entries; i++)
5289   {
5290     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5291
5292     if (pos >= 0 && pos <= scores.last_added)
5293       scores.last_added++;
5294   }
5295
5296   if (scores.last_added >= MAX_SCORE_ENTRIES)
5297   {
5298     scores.last_added = MAX_SCORE_ENTRIES - 1;
5299     scores.force_last_added = TRUE;
5300
5301     scores.entry[scores.last_added] = last_added_entry;
5302   }
5303 }
5304
5305 static int getElementMoveStepsizeExt(int x, int y, int direction)
5306 {
5307   int element = Tile[x][y];
5308   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5309   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5310   int horiz_move = (dx != 0);
5311   int sign = (horiz_move ? dx : dy);
5312   int step = sign * element_info[element].move_stepsize;
5313
5314   // special values for move stepsize for spring and things on conveyor belt
5315   if (horiz_move)
5316   {
5317     if (CAN_FALL(element) &&
5318         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5319       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5320     else if (element == EL_SPRING)
5321       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5322   }
5323
5324   return step;
5325 }
5326
5327 static int getElementMoveStepsize(int x, int y)
5328 {
5329   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5330 }
5331
5332 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5333 {
5334   if (player->GfxAction != action || player->GfxDir != dir)
5335   {
5336     player->GfxAction = action;
5337     player->GfxDir = dir;
5338     player->Frame = 0;
5339     player->StepFrame = 0;
5340   }
5341 }
5342
5343 static void ResetGfxFrame(int x, int y)
5344 {
5345   // profiling showed that "autotest" spends 10~20% of its time in this function
5346   if (DrawingDeactivatedField())
5347     return;
5348
5349   int element = Tile[x][y];
5350   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5351
5352   if (graphic_info[graphic].anim_global_sync)
5353     GfxFrame[x][y] = FrameCounter;
5354   else if (graphic_info[graphic].anim_global_anim_sync)
5355     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5356   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5357     GfxFrame[x][y] = CustomValue[x][y];
5358   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5359     GfxFrame[x][y] = element_info[element].collect_score;
5360   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5361     GfxFrame[x][y] = ChangeDelay[x][y];
5362 }
5363
5364 static void ResetGfxAnimation(int x, int y)
5365 {
5366   GfxAction[x][y] = ACTION_DEFAULT;
5367   GfxDir[x][y] = MovDir[x][y];
5368   GfxFrame[x][y] = 0;
5369
5370   ResetGfxFrame(x, y);
5371 }
5372
5373 static void ResetRandomAnimationValue(int x, int y)
5374 {
5375   GfxRandom[x][y] = INIT_GFX_RANDOM();
5376 }
5377
5378 static void InitMovingField(int x, int y, int direction)
5379 {
5380   int element = Tile[x][y];
5381   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5382   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5383   int newx = x + dx;
5384   int newy = y + dy;
5385   boolean is_moving_before, is_moving_after;
5386
5387   // check if element was/is moving or being moved before/after mode change
5388   is_moving_before = (WasJustMoving[x][y] != 0);
5389   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5390
5391   // reset animation only for moving elements which change direction of moving
5392   // or which just started or stopped moving
5393   // (else CEs with property "can move" / "not moving" are reset each frame)
5394   if (is_moving_before != is_moving_after ||
5395       direction != MovDir[x][y])
5396     ResetGfxAnimation(x, y);
5397
5398   MovDir[x][y] = direction;
5399   GfxDir[x][y] = direction;
5400
5401   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5402                      direction == MV_DOWN && CAN_FALL(element) ?
5403                      ACTION_FALLING : ACTION_MOVING);
5404
5405   // this is needed for CEs with property "can move" / "not moving"
5406
5407   if (is_moving_after)
5408   {
5409     if (Tile[newx][newy] == EL_EMPTY)
5410       Tile[newx][newy] = EL_BLOCKED;
5411
5412     MovDir[newx][newy] = MovDir[x][y];
5413
5414     CustomValue[newx][newy] = CustomValue[x][y];
5415
5416     GfxFrame[newx][newy] = GfxFrame[x][y];
5417     GfxRandom[newx][newy] = GfxRandom[x][y];
5418     GfxAction[newx][newy] = GfxAction[x][y];
5419     GfxDir[newx][newy] = GfxDir[x][y];
5420   }
5421 }
5422
5423 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5424 {
5425   int direction = MovDir[x][y];
5426   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5427   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5428
5429   *goes_to_x = newx;
5430   *goes_to_y = newy;
5431 }
5432
5433 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5434 {
5435   int direction = MovDir[x][y];
5436   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5437   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5438
5439   *comes_from_x = oldx;
5440   *comes_from_y = oldy;
5441 }
5442
5443 static int MovingOrBlocked2Element(int x, int y)
5444 {
5445   int element = Tile[x][y];
5446
5447   if (element == EL_BLOCKED)
5448   {
5449     int oldx, oldy;
5450
5451     Blocked2Moving(x, y, &oldx, &oldy);
5452
5453     return Tile[oldx][oldy];
5454   }
5455
5456   return element;
5457 }
5458
5459 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5460 {
5461   // like MovingOrBlocked2Element(), but if element is moving
5462   // and (x, y) is the field the moving element is just leaving,
5463   // return EL_BLOCKED instead of the element value
5464   int element = Tile[x][y];
5465
5466   if (IS_MOVING(x, y))
5467   {
5468     if (element == EL_BLOCKED)
5469     {
5470       int oldx, oldy;
5471
5472       Blocked2Moving(x, y, &oldx, &oldy);
5473       return Tile[oldx][oldy];
5474     }
5475     else
5476       return EL_BLOCKED;
5477   }
5478   else
5479     return element;
5480 }
5481
5482 static void RemoveField(int x, int y)
5483 {
5484   Tile[x][y] = EL_EMPTY;
5485
5486   MovPos[x][y] = 0;
5487   MovDir[x][y] = 0;
5488   MovDelay[x][y] = 0;
5489
5490   CustomValue[x][y] = 0;
5491
5492   AmoebaNr[x][y] = 0;
5493   ChangeDelay[x][y] = 0;
5494   ChangePage[x][y] = -1;
5495   Pushed[x][y] = FALSE;
5496
5497   GfxElement[x][y] = EL_UNDEFINED;
5498   GfxAction[x][y] = ACTION_DEFAULT;
5499   GfxDir[x][y] = MV_NONE;
5500 }
5501
5502 static void RemoveMovingField(int x, int y)
5503 {
5504   int oldx = x, oldy = y, newx = x, newy = y;
5505   int element = Tile[x][y];
5506   int next_element = EL_UNDEFINED;
5507
5508   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5509     return;
5510
5511   if (IS_MOVING(x, y))
5512   {
5513     Moving2Blocked(x, y, &newx, &newy);
5514
5515     if (Tile[newx][newy] != EL_BLOCKED)
5516     {
5517       // element is moving, but target field is not free (blocked), but
5518       // already occupied by something different (example: acid pool);
5519       // in this case, only remove the moving field, but not the target
5520
5521       RemoveField(oldx, oldy);
5522
5523       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5524
5525       TEST_DrawLevelField(oldx, oldy);
5526
5527       return;
5528     }
5529   }
5530   else if (element == EL_BLOCKED)
5531   {
5532     Blocked2Moving(x, y, &oldx, &oldy);
5533     if (!IS_MOVING(oldx, oldy))
5534       return;
5535   }
5536
5537   if (element == EL_BLOCKED &&
5538       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5539        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5540        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5541        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5542        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5543        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5544     next_element = get_next_element(Tile[oldx][oldy]);
5545
5546   RemoveField(oldx, oldy);
5547   RemoveField(newx, newy);
5548
5549   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5550
5551   if (next_element != EL_UNDEFINED)
5552     Tile[oldx][oldy] = next_element;
5553
5554   TEST_DrawLevelField(oldx, oldy);
5555   TEST_DrawLevelField(newx, newy);
5556 }
5557
5558 void DrawDynamite(int x, int y)
5559 {
5560   int sx = SCREENX(x), sy = SCREENY(y);
5561   int graphic = el2img(Tile[x][y]);
5562   int frame;
5563
5564   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5565     return;
5566
5567   if (IS_WALKABLE_INSIDE(Back[x][y]))
5568     return;
5569
5570   if (Back[x][y])
5571     DrawLevelElement(x, y, Back[x][y]);
5572   else if (Store[x][y])
5573     DrawLevelElement(x, y, Store[x][y]);
5574   else if (game.use_masked_elements)
5575     DrawLevelElement(x, y, EL_EMPTY);
5576
5577   frame = getGraphicAnimationFrameXY(graphic, x, y);
5578
5579   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5580     DrawGraphicThruMask(sx, sy, graphic, frame);
5581   else
5582     DrawGraphic(sx, sy, graphic, frame);
5583 }
5584
5585 static void CheckDynamite(int x, int y)
5586 {
5587   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5588   {
5589     MovDelay[x][y]--;
5590
5591     if (MovDelay[x][y] != 0)
5592     {
5593       DrawDynamite(x, y);
5594       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5595
5596       return;
5597     }
5598   }
5599
5600   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5601
5602   Bang(x, y);
5603 }
5604
5605 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5606 {
5607   boolean num_checked_players = 0;
5608   int i;
5609
5610   for (i = 0; i < MAX_PLAYERS; i++)
5611   {
5612     if (stored_player[i].active)
5613     {
5614       int sx = stored_player[i].jx;
5615       int sy = stored_player[i].jy;
5616
5617       if (num_checked_players == 0)
5618       {
5619         *sx1 = *sx2 = sx;
5620         *sy1 = *sy2 = sy;
5621       }
5622       else
5623       {
5624         *sx1 = MIN(*sx1, sx);
5625         *sy1 = MIN(*sy1, sy);
5626         *sx2 = MAX(*sx2, sx);
5627         *sy2 = MAX(*sy2, sy);
5628       }
5629
5630       num_checked_players++;
5631     }
5632   }
5633 }
5634
5635 static boolean checkIfAllPlayersFitToScreen_RND(void)
5636 {
5637   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5638
5639   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5640
5641   return (sx2 - sx1 < SCR_FIELDX &&
5642           sy2 - sy1 < SCR_FIELDY);
5643 }
5644
5645 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5646 {
5647   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5648
5649   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5650
5651   *sx = (sx1 + sx2) / 2;
5652   *sy = (sy1 + sy2) / 2;
5653 }
5654
5655 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5656                                boolean center_screen, boolean quick_relocation)
5657 {
5658   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5659   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5660   boolean no_delay = (tape.warp_forward);
5661   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5662   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5663   int new_scroll_x, new_scroll_y;
5664
5665   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5666   {
5667     // case 1: quick relocation inside visible screen (without scrolling)
5668
5669     RedrawPlayfield();
5670
5671     return;
5672   }
5673
5674   if (!level.shifted_relocation || center_screen)
5675   {
5676     // relocation _with_ centering of screen
5677
5678     new_scroll_x = SCROLL_POSITION_X(x);
5679     new_scroll_y = SCROLL_POSITION_Y(y);
5680   }
5681   else
5682   {
5683     // relocation _without_ centering of screen
5684
5685     // apply distance between old and new player position to scroll position
5686     int shifted_scroll_x = scroll_x + (x - old_x);
5687     int shifted_scroll_y = scroll_y + (y - old_y);
5688
5689     // make sure that shifted scroll position does not scroll beyond screen
5690     new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX);
5691     new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY);
5692
5693     // special case for teleporting from one end of the playfield to the other
5694     // (this kludge prevents the destination area to be shifted by half a tile
5695     // against the source destination for even screen width or screen height;
5696     // probably most useful when used with high "game.forced_scroll_delay_value"
5697     // in combination with "game.forced_scroll_x" and "game.forced_scroll_y")
5698     if (quick_relocation)
5699     {
5700       if (EVEN(SCR_FIELDX))
5701       {
5702         // relocate (teleport) between left and right border (half or full)
5703         if (scroll_x == SBX_Left && new_scroll_x == SBX_Right - 1)
5704           new_scroll_x = SBX_Right;
5705         else if (scroll_x == SBX_Left + 1 && new_scroll_x == SBX_Right)
5706           new_scroll_x = SBX_Right - 1;
5707         else if (scroll_x == SBX_Right && new_scroll_x == SBX_Left + 1)
5708           new_scroll_x = SBX_Left;
5709         else if (scroll_x == SBX_Right - 1 && new_scroll_x == SBX_Left)
5710           new_scroll_x = SBX_Left + 1;
5711       }
5712
5713       if (EVEN(SCR_FIELDY))
5714       {
5715         // relocate (teleport) between top and bottom border (half or full)
5716         if (scroll_y == SBY_Upper && new_scroll_y == SBY_Lower - 1)
5717           new_scroll_y = SBY_Lower;
5718         else if (scroll_y == SBY_Upper + 1 && new_scroll_y == SBY_Lower)
5719           new_scroll_y = SBY_Lower - 1;
5720         else if (scroll_y == SBY_Lower && new_scroll_y == SBY_Upper + 1)
5721           new_scroll_y = SBY_Upper;
5722         else if (scroll_y == SBY_Lower - 1 && new_scroll_y == SBY_Upper)
5723           new_scroll_y = SBY_Upper + 1;
5724       }
5725     }
5726   }
5727
5728   if (quick_relocation)
5729   {
5730     // case 2: quick relocation (redraw without visible scrolling)
5731
5732     scroll_x = new_scroll_x;
5733     scroll_y = new_scroll_y;
5734
5735     RedrawPlayfield();
5736
5737     return;
5738   }
5739
5740   // case 3: visible relocation (with scrolling to new position)
5741
5742   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5743
5744   SetVideoFrameDelay(wait_delay_value);
5745
5746   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5747   {
5748     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5749     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5750
5751     if (dx == 0 && dy == 0)             // no scrolling needed at all
5752       break;
5753
5754     scroll_x -= dx;
5755     scroll_y -= dy;
5756
5757     // set values for horizontal/vertical screen scrolling (half tile size)
5758     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5759     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5760     int pos_x = dx * TILEX / 2;
5761     int pos_y = dy * TILEY / 2;
5762     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5763     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5764
5765     ScrollLevel(dx, dy);
5766     DrawAllPlayers();
5767
5768     // scroll in two steps of half tile size to make things smoother
5769     BlitScreenToBitmapExt_RND(window, fx, fy);
5770
5771     // scroll second step to align at full tile size
5772     BlitScreenToBitmap(window);
5773   }
5774
5775   DrawAllPlayers();
5776   BackToFront();
5777
5778   SetVideoFrameDelay(frame_delay_value_old);
5779 }
5780
5781 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5782 {
5783   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5784   int player_nr = GET_PLAYER_NR(el_player);
5785   struct PlayerInfo *player = &stored_player[player_nr];
5786   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5787   boolean no_delay = (tape.warp_forward);
5788   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5789   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5790   int old_jx = player->jx;
5791   int old_jy = player->jy;
5792   int old_element = Tile[old_jx][old_jy];
5793   int element = Tile[jx][jy];
5794   boolean player_relocated = (old_jx != jx || old_jy != jy);
5795
5796   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5797   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5798   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5799   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5800   int leave_side_horiz = move_dir_horiz;
5801   int leave_side_vert  = move_dir_vert;
5802   int enter_side = enter_side_horiz | enter_side_vert;
5803   int leave_side = leave_side_horiz | leave_side_vert;
5804
5805   if (player->buried)           // do not reanimate dead player
5806     return;
5807
5808   if (!player_relocated)        // no need to relocate the player
5809     return;
5810
5811   if (IS_PLAYER(jx, jy))        // player already placed at new position
5812   {
5813     RemoveField(jx, jy);        // temporarily remove newly placed player
5814     DrawLevelField(jx, jy);
5815   }
5816
5817   if (player->present)
5818   {
5819     while (player->MovPos)
5820     {
5821       ScrollPlayer(player, SCROLL_GO_ON);
5822       ScrollScreen(NULL, SCROLL_GO_ON);
5823
5824       AdvanceFrameAndPlayerCounters(player->index_nr);
5825
5826       DrawPlayer(player);
5827
5828       BackToFront_WithFrameDelay(wait_delay_value);
5829     }
5830
5831     DrawPlayer(player);         // needed here only to cleanup last field
5832     DrawLevelField(player->jx, player->jy);     // remove player graphic
5833
5834     player->is_moving = FALSE;
5835   }
5836
5837   if (IS_CUSTOM_ELEMENT(old_element))
5838     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5839                                CE_LEFT_BY_PLAYER,
5840                                player->index_bit, leave_side);
5841
5842   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5843                                       CE_PLAYER_LEAVES_X,
5844                                       player->index_bit, leave_side);
5845
5846   Tile[jx][jy] = el_player;
5847   InitPlayerField(jx, jy, el_player, TRUE);
5848
5849   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5850      possible that the relocation target field did not contain a player element,
5851      but a walkable element, to which the new player was relocated -- in this
5852      case, restore that (already initialized!) element on the player field */
5853   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5854   {
5855     Tile[jx][jy] = element;     // restore previously existing element
5856   }
5857
5858   // only visually relocate centered player
5859   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5860                      FALSE, level.instant_relocation);
5861
5862   TestIfPlayerTouchesBadThing(jx, jy);
5863   TestIfPlayerTouchesCustomElement(jx, jy);
5864
5865   if (IS_CUSTOM_ELEMENT(element))
5866     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5867                                player->index_bit, enter_side);
5868
5869   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5870                                       player->index_bit, enter_side);
5871
5872   if (player->is_switching)
5873   {
5874     /* ensure that relocation while still switching an element does not cause
5875        a new element to be treated as also switched directly after relocation
5876        (this is important for teleporter switches that teleport the player to
5877        a place where another teleporter switch is in the same direction, which
5878        would then incorrectly be treated as immediately switched before the
5879        direction key that caused the switch was released) */
5880
5881     player->switch_x += jx - old_jx;
5882     player->switch_y += jy - old_jy;
5883   }
5884 }
5885
5886 static void Explode(int ex, int ey, int phase, int mode)
5887 {
5888   int x, y;
5889   int last_phase;
5890   int border_element;
5891
5892   if (game.explosions_delayed)
5893   {
5894     ExplodeField[ex][ey] = mode;
5895     return;
5896   }
5897
5898   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5899   {
5900     int center_element = Tile[ex][ey];
5901     int ce_value = CustomValue[ex][ey];
5902     int ce_score = element_info[center_element].collect_score;
5903     int artwork_element, explosion_element;     // set these values later
5904
5905     // remove things displayed in background while burning dynamite
5906     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5907       Back[ex][ey] = 0;
5908
5909     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5910     {
5911       // put moving element to center field (and let it explode there)
5912       center_element = MovingOrBlocked2Element(ex, ey);
5913       RemoveMovingField(ex, ey);
5914       Tile[ex][ey] = center_element;
5915     }
5916
5917     // now "center_element" is finally determined -- set related values now
5918     artwork_element = center_element;           // for custom player artwork
5919     explosion_element = center_element;         // for custom player artwork
5920
5921     if (IS_PLAYER(ex, ey))
5922     {
5923       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5924
5925       artwork_element = stored_player[player_nr].artwork_element;
5926
5927       if (level.use_explosion_element[player_nr])
5928       {
5929         explosion_element = level.explosion_element[player_nr];
5930         artwork_element = explosion_element;
5931       }
5932     }
5933
5934     if (mode == EX_TYPE_NORMAL ||
5935         mode == EX_TYPE_CENTER ||
5936         mode == EX_TYPE_CROSS)
5937       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5938
5939     last_phase = element_info[explosion_element].explosion_delay + 1;
5940
5941     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5942     {
5943       int xx = x - ex + 1;
5944       int yy = y - ey + 1;
5945       int element;
5946
5947       if (!IN_LEV_FIELD(x, y) ||
5948           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5949           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5950         continue;
5951
5952       element = Tile[x][y];
5953
5954       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5955       {
5956         element = MovingOrBlocked2Element(x, y);
5957
5958         if (!IS_EXPLOSION_PROOF(element))
5959           RemoveMovingField(x, y);
5960       }
5961
5962       // indestructible elements can only explode in center (but not flames)
5963       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5964                                            mode == EX_TYPE_BORDER)) ||
5965           element == EL_FLAMES)
5966         continue;
5967
5968       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5969          behaviour, for example when touching a yamyam that explodes to rocks
5970          with active deadly shield, a rock is created under the player !!! */
5971       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5972 #if 0
5973       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5974           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5975            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5976 #else
5977       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5978 #endif
5979       {
5980         if (IS_ACTIVE_BOMB(element))
5981         {
5982           // re-activate things under the bomb like gate or penguin
5983           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5984           Back[x][y] = 0;
5985         }
5986
5987         continue;
5988       }
5989
5990       // save walkable background elements while explosion on same tile
5991       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5992           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5993         Back[x][y] = element;
5994
5995       // ignite explodable elements reached by other explosion
5996       if (element == EL_EXPLOSION)
5997         element = Store2[x][y];
5998
5999       if (AmoebaNr[x][y] &&
6000           (element == EL_AMOEBA_FULL ||
6001            element == EL_BD_AMOEBA ||
6002            element == EL_AMOEBA_GROWING))
6003       {
6004         AmoebaCnt[AmoebaNr[x][y]]--;
6005         AmoebaCnt2[AmoebaNr[x][y]]--;
6006       }
6007
6008       RemoveField(x, y);
6009
6010       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6011       {
6012         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6013
6014         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6015
6016         if (PLAYERINFO(ex, ey)->use_murphy)
6017           Store[x][y] = EL_EMPTY;
6018       }
6019
6020       // !!! check this case -- currently needed for rnd_rado_negundo_v,
6021       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
6022       else if (IS_PLAYER_ELEMENT(center_element))
6023         Store[x][y] = EL_EMPTY;
6024       else if (center_element == EL_YAMYAM)
6025         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6026       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6027         Store[x][y] = element_info[center_element].content.e[xx][yy];
6028 #if 1
6029       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6030       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6031       // otherwise) -- FIX THIS !!!
6032       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6033         Store[x][y] = element_info[element].content.e[1][1];
6034 #else
6035       else if (!CAN_EXPLODE(element))
6036         Store[x][y] = element_info[element].content.e[1][1];
6037 #endif
6038       else
6039         Store[x][y] = EL_EMPTY;
6040
6041       if (IS_CUSTOM_ELEMENT(center_element))
6042         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
6043                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
6044                        Store[x][y] >= EL_PREV_CE_8 &&
6045                        Store[x][y] <= EL_NEXT_CE_8 ?
6046                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
6047                        Store[x][y]);
6048
6049       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6050           center_element == EL_AMOEBA_TO_DIAMOND)
6051         Store2[x][y] = element;
6052
6053       Tile[x][y] = EL_EXPLOSION;
6054       GfxElement[x][y] = artwork_element;
6055
6056       ExplodePhase[x][y] = 1;
6057       ExplodeDelay[x][y] = last_phase;
6058
6059       Stop[x][y] = TRUE;
6060     }
6061
6062     if (center_element == EL_YAMYAM)
6063       game.yamyam_content_nr =
6064         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6065
6066     return;
6067   }
6068
6069   if (Stop[ex][ey])
6070     return;
6071
6072   x = ex;
6073   y = ey;
6074
6075   if (phase == 1)
6076     GfxFrame[x][y] = 0;         // restart explosion animation
6077
6078   last_phase = ExplodeDelay[x][y];
6079
6080   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6081
6082   // this can happen if the player leaves an explosion just in time
6083   if (GfxElement[x][y] == EL_UNDEFINED)
6084     GfxElement[x][y] = EL_EMPTY;
6085
6086   border_element = Store2[x][y];
6087   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6088     border_element = StorePlayer[x][y];
6089
6090   if (phase == element_info[border_element].ignition_delay ||
6091       phase == last_phase)
6092   {
6093     boolean border_explosion = FALSE;
6094
6095     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6096         !PLAYER_EXPLOSION_PROTECTED(x, y))
6097     {
6098       KillPlayerUnlessExplosionProtected(x, y);
6099       border_explosion = TRUE;
6100     }
6101     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6102     {
6103       Tile[x][y] = Store2[x][y];
6104       Store2[x][y] = 0;
6105       Bang(x, y);
6106       border_explosion = TRUE;
6107     }
6108     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6109     {
6110       AmoebaToDiamond(x, y);
6111       Store2[x][y] = 0;
6112       border_explosion = TRUE;
6113     }
6114
6115     // if an element just explodes due to another explosion (chain-reaction),
6116     // do not immediately end the new explosion when it was the last frame of
6117     // the explosion (as it would be done in the following "if"-statement!)
6118     if (border_explosion && phase == last_phase)
6119       return;
6120   }
6121
6122   // this can happen if the player was just killed by an explosion
6123   if (GfxElement[x][y] == EL_UNDEFINED)
6124     GfxElement[x][y] = EL_EMPTY;
6125
6126   if (phase == last_phase)
6127   {
6128     int element;
6129
6130     element = Tile[x][y] = Store[x][y];
6131     Store[x][y] = Store2[x][y] = 0;
6132     GfxElement[x][y] = EL_UNDEFINED;
6133
6134     // player can escape from explosions and might therefore be still alive
6135     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6136         element <= EL_PLAYER_IS_EXPLODING_4)
6137     {
6138       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6139       int explosion_element = EL_PLAYER_1 + player_nr;
6140       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6141       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6142
6143       if (level.use_explosion_element[player_nr])
6144         explosion_element = level.explosion_element[player_nr];
6145
6146       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6147                     element_info[explosion_element].content.e[xx][yy]);
6148     }
6149
6150     // restore probably existing indestructible background element
6151     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6152       element = Tile[x][y] = Back[x][y];
6153     Back[x][y] = 0;
6154
6155     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6156     GfxDir[x][y] = MV_NONE;
6157     ChangeDelay[x][y] = 0;
6158     ChangePage[x][y] = -1;
6159
6160     CustomValue[x][y] = 0;
6161
6162     InitField_WithBug2(x, y, FALSE);
6163
6164     TEST_DrawLevelField(x, y);
6165
6166     TestIfElementTouchesCustomElement(x, y);
6167
6168     if (GFX_CRUMBLED(element))
6169       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6170
6171     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6172       StorePlayer[x][y] = 0;
6173
6174     if (IS_PLAYER_ELEMENT(element))
6175       RelocatePlayer(x, y, element);
6176   }
6177   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6178   {
6179     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6180     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6181
6182     if (phase == 1)
6183       TEST_DrawLevelFieldCrumbled(x, y);
6184
6185     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6186     {
6187       DrawLevelElement(x, y, Back[x][y]);
6188       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6189     }
6190     else if (IS_WALKABLE_UNDER(Back[x][y]))
6191     {
6192       DrawLevelGraphic(x, y, graphic, frame);
6193       DrawLevelElementThruMask(x, y, Back[x][y]);
6194     }
6195     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6196       DrawLevelGraphic(x, y, graphic, frame);
6197   }
6198 }
6199
6200 static void DynaExplode(int ex, int ey)
6201 {
6202   int i, j;
6203   int dynabomb_element = Tile[ex][ey];
6204   int dynabomb_size = 1;
6205   boolean dynabomb_xl = FALSE;
6206   struct PlayerInfo *player;
6207   struct XY *xy = xy_topdown;
6208
6209   if (IS_ACTIVE_BOMB(dynabomb_element))
6210   {
6211     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6212     dynabomb_size = player->dynabomb_size;
6213     dynabomb_xl = player->dynabomb_xl;
6214     player->dynabombs_left++;
6215   }
6216
6217   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6218
6219   for (i = 0; i < NUM_DIRECTIONS; i++)
6220   {
6221     for (j = 1; j <= dynabomb_size; j++)
6222     {
6223       int x = ex + j * xy[i].x;
6224       int y = ey + j * xy[i].y;
6225       int element;
6226
6227       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6228         break;
6229
6230       element = Tile[x][y];
6231
6232       // do not restart explosions of fields with active bombs
6233       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6234         continue;
6235
6236       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6237
6238       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6239           !IS_DIGGABLE(element) && !dynabomb_xl)
6240         break;
6241     }
6242   }
6243 }
6244
6245 void Bang(int x, int y)
6246 {
6247   int element = MovingOrBlocked2Element(x, y);
6248   int explosion_type = EX_TYPE_NORMAL;
6249
6250   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6251   {
6252     struct PlayerInfo *player = PLAYERINFO(x, y);
6253
6254     element = Tile[x][y] = player->initial_element;
6255
6256     if (level.use_explosion_element[player->index_nr])
6257     {
6258       int explosion_element = level.explosion_element[player->index_nr];
6259
6260       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6261         explosion_type = EX_TYPE_CROSS;
6262       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6263         explosion_type = EX_TYPE_CENTER;
6264     }
6265   }
6266
6267   switch (element)
6268   {
6269     case EL_BUG:
6270     case EL_SPACESHIP:
6271     case EL_BD_BUTTERFLY:
6272     case EL_BD_FIREFLY:
6273     case EL_YAMYAM:
6274     case EL_DARK_YAMYAM:
6275     case EL_ROBOT:
6276     case EL_PACMAN:
6277     case EL_MOLE:
6278       RaiseScoreElement(element);
6279       break;
6280
6281     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6282     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6283     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6284     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6285     case EL_DYNABOMB_INCREASE_NUMBER:
6286     case EL_DYNABOMB_INCREASE_SIZE:
6287     case EL_DYNABOMB_INCREASE_POWER:
6288       explosion_type = EX_TYPE_DYNA;
6289       break;
6290
6291     case EL_DC_LANDMINE:
6292       explosion_type = EX_TYPE_CENTER;
6293       break;
6294
6295     case EL_PENGUIN:
6296     case EL_LAMP:
6297     case EL_LAMP_ACTIVE:
6298     case EL_AMOEBA_TO_DIAMOND:
6299       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6300         explosion_type = EX_TYPE_CENTER;
6301       break;
6302
6303     default:
6304       if (element_info[element].explosion_type == EXPLODES_CROSS)
6305         explosion_type = EX_TYPE_CROSS;
6306       else if (element_info[element].explosion_type == EXPLODES_1X1)
6307         explosion_type = EX_TYPE_CENTER;
6308       break;
6309   }
6310
6311   if (explosion_type == EX_TYPE_DYNA)
6312     DynaExplode(x, y);
6313   else
6314     Explode(x, y, EX_PHASE_START, explosion_type);
6315
6316   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6317 }
6318
6319 static void SplashAcid(int x, int y)
6320 {
6321   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6322       (!IN_LEV_FIELD(x - 1, y - 2) ||
6323        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6324     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6325
6326   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6327       (!IN_LEV_FIELD(x + 1, y - 2) ||
6328        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6329     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6330
6331   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6332 }
6333
6334 static void InitBeltMovement(void)
6335 {
6336   static int belt_base_element[4] =
6337   {
6338     EL_CONVEYOR_BELT_1_LEFT,
6339     EL_CONVEYOR_BELT_2_LEFT,
6340     EL_CONVEYOR_BELT_3_LEFT,
6341     EL_CONVEYOR_BELT_4_LEFT
6342   };
6343   static int belt_base_active_element[4] =
6344   {
6345     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6346     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6347     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6348     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6349   };
6350
6351   int x, y, i, j;
6352
6353   // set frame order for belt animation graphic according to belt direction
6354   for (i = 0; i < NUM_BELTS; i++)
6355   {
6356     int belt_nr = i;
6357
6358     for (j = 0; j < NUM_BELT_PARTS; j++)
6359     {
6360       int element = belt_base_active_element[belt_nr] + j;
6361       int graphic_1 = el2img(element);
6362       int graphic_2 = el2panelimg(element);
6363
6364       if (game.belt_dir[i] == MV_LEFT)
6365       {
6366         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6367         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6368       }
6369       else
6370       {
6371         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6372         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6373       }
6374     }
6375   }
6376
6377   SCAN_PLAYFIELD(x, y)
6378   {
6379     int element = Tile[x][y];
6380
6381     for (i = 0; i < NUM_BELTS; i++)
6382     {
6383       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6384       {
6385         int e_belt_nr = getBeltNrFromBeltElement(element);
6386         int belt_nr = i;
6387
6388         if (e_belt_nr == belt_nr)
6389         {
6390           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6391
6392           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6393         }
6394       }
6395     }
6396   }
6397 }
6398
6399 static void ToggleBeltSwitch(int x, int y)
6400 {
6401   static int belt_base_element[4] =
6402   {
6403     EL_CONVEYOR_BELT_1_LEFT,
6404     EL_CONVEYOR_BELT_2_LEFT,
6405     EL_CONVEYOR_BELT_3_LEFT,
6406     EL_CONVEYOR_BELT_4_LEFT
6407   };
6408   static int belt_base_active_element[4] =
6409   {
6410     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6411     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6412     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6413     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6414   };
6415   static int belt_base_switch_element[4] =
6416   {
6417     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6418     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6419     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6420     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6421   };
6422   static int belt_move_dir[4] =
6423   {
6424     MV_LEFT,
6425     MV_NONE,
6426     MV_RIGHT,
6427     MV_NONE,
6428   };
6429
6430   int element = Tile[x][y];
6431   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6432   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6433   int belt_dir = belt_move_dir[belt_dir_nr];
6434   int xx, yy, i;
6435
6436   if (!IS_BELT_SWITCH(element))
6437     return;
6438
6439   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6440   game.belt_dir[belt_nr] = belt_dir;
6441
6442   if (belt_dir_nr == 3)
6443     belt_dir_nr = 1;
6444
6445   // set frame order for belt animation graphic according to belt direction
6446   for (i = 0; i < NUM_BELT_PARTS; i++)
6447   {
6448     int element = belt_base_active_element[belt_nr] + i;
6449     int graphic_1 = el2img(element);
6450     int graphic_2 = el2panelimg(element);
6451
6452     if (belt_dir == MV_LEFT)
6453     {
6454       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6455       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6456     }
6457     else
6458     {
6459       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6460       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6461     }
6462   }
6463
6464   SCAN_PLAYFIELD(xx, yy)
6465   {
6466     int element = Tile[xx][yy];
6467
6468     if (IS_BELT_SWITCH(element))
6469     {
6470       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6471
6472       if (e_belt_nr == belt_nr)
6473       {
6474         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6475         TEST_DrawLevelField(xx, yy);
6476       }
6477     }
6478     else if (IS_BELT(element) && belt_dir != MV_NONE)
6479     {
6480       int e_belt_nr = getBeltNrFromBeltElement(element);
6481
6482       if (e_belt_nr == belt_nr)
6483       {
6484         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6485
6486         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6487         TEST_DrawLevelField(xx, yy);
6488       }
6489     }
6490     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6491     {
6492       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6493
6494       if (e_belt_nr == belt_nr)
6495       {
6496         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6497
6498         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6499         TEST_DrawLevelField(xx, yy);
6500       }
6501     }
6502   }
6503 }
6504
6505 static void ToggleSwitchgateSwitch(void)
6506 {
6507   int xx, yy;
6508
6509   game.switchgate_pos = !game.switchgate_pos;
6510
6511   SCAN_PLAYFIELD(xx, yy)
6512   {
6513     int element = Tile[xx][yy];
6514
6515     if (element == EL_SWITCHGATE_SWITCH_UP)
6516     {
6517       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6518       TEST_DrawLevelField(xx, yy);
6519     }
6520     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6521     {
6522       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6523       TEST_DrawLevelField(xx, yy);
6524     }
6525     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6526     {
6527       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6528       TEST_DrawLevelField(xx, yy);
6529     }
6530     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6531     {
6532       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6533       TEST_DrawLevelField(xx, yy);
6534     }
6535     else if (element == EL_SWITCHGATE_OPEN ||
6536              element == EL_SWITCHGATE_OPENING)
6537     {
6538       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6539
6540       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6541     }
6542     else if (element == EL_SWITCHGATE_CLOSED ||
6543              element == EL_SWITCHGATE_CLOSING)
6544     {
6545       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6546
6547       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6548     }
6549   }
6550 }
6551
6552 static int getInvisibleActiveFromInvisibleElement(int element)
6553 {
6554   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6555           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6556           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6557           element);
6558 }
6559
6560 static int getInvisibleFromInvisibleActiveElement(int element)
6561 {
6562   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6563           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6564           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6565           element);
6566 }
6567
6568 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6569 {
6570   int x, y;
6571
6572   SCAN_PLAYFIELD(x, y)
6573   {
6574     int element = Tile[x][y];
6575
6576     if (element == EL_LIGHT_SWITCH &&
6577         game.light_time_left > 0)
6578     {
6579       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6580       TEST_DrawLevelField(x, y);
6581     }
6582     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6583              game.light_time_left == 0)
6584     {
6585       Tile[x][y] = EL_LIGHT_SWITCH;
6586       TEST_DrawLevelField(x, y);
6587     }
6588     else if (element == EL_EMC_DRIPPER &&
6589              game.light_time_left > 0)
6590     {
6591       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6592       TEST_DrawLevelField(x, y);
6593     }
6594     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6595              game.light_time_left == 0)
6596     {
6597       Tile[x][y] = EL_EMC_DRIPPER;
6598       TEST_DrawLevelField(x, y);
6599     }
6600     else if (element == EL_INVISIBLE_STEELWALL ||
6601              element == EL_INVISIBLE_WALL ||
6602              element == EL_INVISIBLE_SAND)
6603     {
6604       if (game.light_time_left > 0)
6605         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6606
6607       TEST_DrawLevelField(x, y);
6608
6609       // uncrumble neighbour fields, if needed
6610       if (element == EL_INVISIBLE_SAND)
6611         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6612     }
6613     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6614              element == EL_INVISIBLE_WALL_ACTIVE ||
6615              element == EL_INVISIBLE_SAND_ACTIVE)
6616     {
6617       if (game.light_time_left == 0)
6618         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6619
6620       TEST_DrawLevelField(x, y);
6621
6622       // re-crumble neighbour fields, if needed
6623       if (element == EL_INVISIBLE_SAND)
6624         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6625     }
6626   }
6627 }
6628
6629 static void RedrawAllInvisibleElementsForLenses(void)
6630 {
6631   int x, y;
6632
6633   SCAN_PLAYFIELD(x, y)
6634   {
6635     int element = Tile[x][y];
6636
6637     if (element == EL_EMC_DRIPPER &&
6638         game.lenses_time_left > 0)
6639     {
6640       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6641       TEST_DrawLevelField(x, y);
6642     }
6643     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6644              game.lenses_time_left == 0)
6645     {
6646       Tile[x][y] = EL_EMC_DRIPPER;
6647       TEST_DrawLevelField(x, y);
6648     }
6649     else if (element == EL_INVISIBLE_STEELWALL ||
6650              element == EL_INVISIBLE_WALL ||
6651              element == EL_INVISIBLE_SAND)
6652     {
6653       if (game.lenses_time_left > 0)
6654         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6655
6656       TEST_DrawLevelField(x, y);
6657
6658       // uncrumble neighbour fields, if needed
6659       if (element == EL_INVISIBLE_SAND)
6660         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6661     }
6662     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6663              element == EL_INVISIBLE_WALL_ACTIVE ||
6664              element == EL_INVISIBLE_SAND_ACTIVE)
6665     {
6666       if (game.lenses_time_left == 0)
6667         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6668
6669       TEST_DrawLevelField(x, y);
6670
6671       // re-crumble neighbour fields, if needed
6672       if (element == EL_INVISIBLE_SAND)
6673         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6674     }
6675   }
6676 }
6677
6678 static void RedrawAllInvisibleElementsForMagnifier(void)
6679 {
6680   int x, y;
6681
6682   SCAN_PLAYFIELD(x, y)
6683   {
6684     int element = Tile[x][y];
6685
6686     if (element == EL_EMC_FAKE_GRASS &&
6687         game.magnify_time_left > 0)
6688     {
6689       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6690       TEST_DrawLevelField(x, y);
6691     }
6692     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6693              game.magnify_time_left == 0)
6694     {
6695       Tile[x][y] = EL_EMC_FAKE_GRASS;
6696       TEST_DrawLevelField(x, y);
6697     }
6698     else if (IS_GATE_GRAY(element) &&
6699              game.magnify_time_left > 0)
6700     {
6701       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6702                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6703                     IS_EM_GATE_GRAY(element) ?
6704                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6705                     IS_EMC_GATE_GRAY(element) ?
6706                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6707                     IS_DC_GATE_GRAY(element) ?
6708                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6709                     element);
6710       TEST_DrawLevelField(x, y);
6711     }
6712     else if (IS_GATE_GRAY_ACTIVE(element) &&
6713              game.magnify_time_left == 0)
6714     {
6715       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6716                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6717                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6718                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6719                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6720                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6721                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6722                     EL_DC_GATE_WHITE_GRAY :
6723                     element);
6724       TEST_DrawLevelField(x, y);
6725     }
6726   }
6727 }
6728
6729 static void ToggleLightSwitch(int x, int y)
6730 {
6731   int element = Tile[x][y];
6732
6733   game.light_time_left =
6734     (element == EL_LIGHT_SWITCH ?
6735      level.time_light * FRAMES_PER_SECOND : 0);
6736
6737   RedrawAllLightSwitchesAndInvisibleElements();
6738 }
6739
6740 static void ActivateTimegateSwitch(int x, int y)
6741 {
6742   int xx, yy;
6743
6744   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6745
6746   SCAN_PLAYFIELD(xx, yy)
6747   {
6748     int element = Tile[xx][yy];
6749
6750     if (element == EL_TIMEGATE_CLOSED ||
6751         element == EL_TIMEGATE_CLOSING)
6752     {
6753       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6754       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6755     }
6756
6757     /*
6758     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6759     {
6760       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6761       TEST_DrawLevelField(xx, yy);
6762     }
6763     */
6764
6765   }
6766
6767   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6768                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6769 }
6770
6771 static void Impact(int x, int y)
6772 {
6773   boolean last_line = (y == lev_fieldy - 1);
6774   boolean object_hit = FALSE;
6775   boolean impact = (last_line || object_hit);
6776   int element = Tile[x][y];
6777   int smashed = EL_STEELWALL;
6778
6779   if (!last_line)       // check if element below was hit
6780   {
6781     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6782       return;
6783
6784     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6785                                          MovDir[x][y + 1] != MV_DOWN ||
6786                                          MovPos[x][y + 1] <= TILEY / 2));
6787
6788     // do not smash moving elements that left the smashed field in time
6789     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6790         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6791       object_hit = FALSE;
6792
6793 #if USE_QUICKSAND_IMPACT_BUGFIX
6794     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6795     {
6796       RemoveMovingField(x, y + 1);
6797       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6798       Tile[x][y + 2] = EL_ROCK;
6799       TEST_DrawLevelField(x, y + 2);
6800
6801       object_hit = TRUE;
6802     }
6803
6804     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6805     {
6806       RemoveMovingField(x, y + 1);
6807       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6808       Tile[x][y + 2] = EL_ROCK;
6809       TEST_DrawLevelField(x, y + 2);
6810
6811       object_hit = TRUE;
6812     }
6813 #endif
6814
6815     if (object_hit)
6816       smashed = MovingOrBlocked2Element(x, y + 1);
6817
6818     impact = (last_line || object_hit);
6819   }
6820
6821   if (!last_line && smashed == EL_ACID) // element falls into acid
6822   {
6823     SplashAcid(x, y + 1);
6824     return;
6825   }
6826
6827   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6828   // only reset graphic animation if graphic really changes after impact
6829   if (impact &&
6830       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6831   {
6832     ResetGfxAnimation(x, y);
6833     TEST_DrawLevelField(x, y);
6834   }
6835
6836   if (impact && CAN_EXPLODE_IMPACT(element))
6837   {
6838     Bang(x, y);
6839     return;
6840   }
6841   else if (impact && element == EL_PEARL &&
6842            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6843   {
6844     ResetGfxAnimation(x, y);
6845
6846     Tile[x][y] = EL_PEARL_BREAKING;
6847     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6848     return;
6849   }
6850   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6851   {
6852     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6853
6854     return;
6855   }
6856
6857   if (impact && element == EL_AMOEBA_DROP)
6858   {
6859     if (object_hit && IS_PLAYER(x, y + 1))
6860       KillPlayerUnlessEnemyProtected(x, y + 1);
6861     else if (object_hit && smashed == EL_PENGUIN)
6862       Bang(x, y + 1);
6863     else
6864     {
6865       Tile[x][y] = EL_AMOEBA_GROWING;
6866       Store[x][y] = EL_AMOEBA_WET;
6867
6868       ResetRandomAnimationValue(x, y);
6869     }
6870     return;
6871   }
6872
6873   if (object_hit)               // check which object was hit
6874   {
6875     if ((CAN_PASS_MAGIC_WALL(element) && 
6876          (smashed == EL_MAGIC_WALL ||
6877           smashed == EL_BD_MAGIC_WALL)) ||
6878         (CAN_PASS_DC_MAGIC_WALL(element) &&
6879          smashed == EL_DC_MAGIC_WALL))
6880     {
6881       int xx, yy;
6882       int activated_magic_wall =
6883         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6884          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6885          EL_DC_MAGIC_WALL_ACTIVE);
6886
6887       // activate magic wall / mill
6888       SCAN_PLAYFIELD(xx, yy)
6889       {
6890         if (Tile[xx][yy] == smashed)
6891           Tile[xx][yy] = activated_magic_wall;
6892       }
6893
6894       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6895       game.magic_wall_active = TRUE;
6896
6897       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6898                             SND_MAGIC_WALL_ACTIVATING :
6899                             smashed == EL_BD_MAGIC_WALL ?
6900                             SND_BD_MAGIC_WALL_ACTIVATING :
6901                             SND_DC_MAGIC_WALL_ACTIVATING));
6902     }
6903
6904     if (IS_PLAYER(x, y + 1))
6905     {
6906       if (CAN_SMASH_PLAYER(element))
6907       {
6908         KillPlayerUnlessEnemyProtected(x, y + 1);
6909         return;
6910       }
6911     }
6912     else if (smashed == EL_PENGUIN)
6913     {
6914       if (CAN_SMASH_PLAYER(element))
6915       {
6916         Bang(x, y + 1);
6917         return;
6918       }
6919     }
6920     else if (element == EL_BD_DIAMOND)
6921     {
6922       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6923       {
6924         Bang(x, y + 1);
6925         return;
6926       }
6927     }
6928     else if (((element == EL_SP_INFOTRON ||
6929                element == EL_SP_ZONK) &&
6930               (smashed == EL_SP_SNIKSNAK ||
6931                smashed == EL_SP_ELECTRON ||
6932                smashed == EL_SP_DISK_ORANGE)) ||
6933              (element == EL_SP_INFOTRON &&
6934               smashed == EL_SP_DISK_YELLOW))
6935     {
6936       Bang(x, y + 1);
6937       return;
6938     }
6939     else if (CAN_SMASH_EVERYTHING(element))
6940     {
6941       if (IS_CLASSIC_ENEMY(smashed) ||
6942           CAN_EXPLODE_SMASHED(smashed))
6943       {
6944         Bang(x, y + 1);
6945         return;
6946       }
6947       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6948       {
6949         if (smashed == EL_LAMP ||
6950             smashed == EL_LAMP_ACTIVE)
6951         {
6952           Bang(x, y + 1);
6953           return;
6954         }
6955         else if (smashed == EL_NUT)
6956         {
6957           Tile[x][y + 1] = EL_NUT_BREAKING;
6958           PlayLevelSound(x, y, SND_NUT_BREAKING);
6959           RaiseScoreElement(EL_NUT);
6960           return;
6961         }
6962         else if (smashed == EL_PEARL)
6963         {
6964           ResetGfxAnimation(x, y);
6965
6966           Tile[x][y + 1] = EL_PEARL_BREAKING;
6967           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6968           return;
6969         }
6970         else if (smashed == EL_DIAMOND)
6971         {
6972           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6973           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6974           return;
6975         }
6976         else if (IS_BELT_SWITCH(smashed))
6977         {
6978           ToggleBeltSwitch(x, y + 1);
6979         }
6980         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6981                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6982                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6983                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6984         {
6985           ToggleSwitchgateSwitch();
6986         }
6987         else if (smashed == EL_LIGHT_SWITCH ||
6988                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6989         {
6990           ToggleLightSwitch(x, y + 1);
6991         }
6992         else
6993         {
6994           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6995
6996           CheckElementChangeBySide(x, y + 1, smashed, element,
6997                                    CE_SWITCHED, CH_SIDE_TOP);
6998           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6999                                             CH_SIDE_TOP);
7000         }
7001       }
7002       else
7003       {
7004         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7005       }
7006     }
7007   }
7008
7009   // play sound of magic wall / mill
7010   if (!last_line &&
7011       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7012        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7013        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7014   {
7015     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7016       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7017     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7018       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7019     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7020       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7021
7022     return;
7023   }
7024
7025   // play sound of object that hits the ground
7026   if (last_line || object_hit)
7027     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7028 }
7029
7030 static void TurnRoundExt(int x, int y)
7031 {
7032   static struct
7033   {
7034     int dx, dy;
7035   } move_xy[] =
7036   {
7037     {  0,  0 },
7038     { -1,  0 },
7039     { +1,  0 },
7040     {  0,  0 },
7041     {  0, -1 },
7042     {  0,  0 }, { 0, 0 }, { 0, 0 },
7043     {  0, +1 }
7044   };
7045   static struct
7046   {
7047     int left, right, back;
7048   } turn[] =
7049   {
7050     { 0,        0,              0        },
7051     { MV_DOWN,  MV_UP,          MV_RIGHT },
7052     { MV_UP,    MV_DOWN,        MV_LEFT  },
7053     { 0,        0,              0        },
7054     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7055     { 0,        0,              0        },
7056     { 0,        0,              0        },
7057     { 0,        0,              0        },
7058     { MV_RIGHT, MV_LEFT,        MV_UP    }
7059   };
7060
7061   int element = Tile[x][y];
7062   int move_pattern = element_info[element].move_pattern;
7063
7064   int old_move_dir = MovDir[x][y];
7065   int left_dir  = turn[old_move_dir].left;
7066   int right_dir = turn[old_move_dir].right;
7067   int back_dir  = turn[old_move_dir].back;
7068
7069   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7070   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7071   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7072   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7073
7074   int left_x  = x + left_dx,  left_y  = y + left_dy;
7075   int right_x = x + right_dx, right_y = y + right_dy;
7076   int move_x  = x + move_dx,  move_y  = y + move_dy;
7077
7078   int xx, yy;
7079
7080   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7081   {
7082     TestIfBadThingTouchesOtherBadThing(x, y);
7083
7084     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7085       MovDir[x][y] = right_dir;
7086     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7087       MovDir[x][y] = left_dir;
7088
7089     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7090       MovDelay[x][y] = 9;
7091     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7092       MovDelay[x][y] = 1;
7093   }
7094   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7095   {
7096     TestIfBadThingTouchesOtherBadThing(x, y);
7097
7098     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7099       MovDir[x][y] = left_dir;
7100     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7101       MovDir[x][y] = right_dir;
7102
7103     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7104       MovDelay[x][y] = 9;
7105     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7106       MovDelay[x][y] = 1;
7107   }
7108   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7109   {
7110     TestIfBadThingTouchesOtherBadThing(x, y);
7111
7112     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7113       MovDir[x][y] = left_dir;
7114     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7115       MovDir[x][y] = right_dir;
7116
7117     if (MovDir[x][y] != old_move_dir)
7118       MovDelay[x][y] = 9;
7119   }
7120   else if (element == EL_YAMYAM)
7121   {
7122     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7123     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7124
7125     if (can_turn_left && can_turn_right)
7126       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7127     else if (can_turn_left)
7128       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7129     else if (can_turn_right)
7130       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7131     else
7132       MovDir[x][y] = back_dir;
7133
7134     MovDelay[x][y] = 16 + 16 * RND(3);
7135   }
7136   else if (element == EL_DARK_YAMYAM)
7137   {
7138     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7139                                                          left_x, left_y);
7140     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7141                                                          right_x, right_y);
7142
7143     if (can_turn_left && can_turn_right)
7144       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7145     else if (can_turn_left)
7146       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7147     else if (can_turn_right)
7148       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7149     else
7150       MovDir[x][y] = back_dir;
7151
7152     MovDelay[x][y] = 16 + 16 * RND(3);
7153   }
7154   else if (element == EL_PACMAN)
7155   {
7156     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7157     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7158
7159     if (can_turn_left && can_turn_right)
7160       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7161     else if (can_turn_left)
7162       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7163     else if (can_turn_right)
7164       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7165     else
7166       MovDir[x][y] = back_dir;
7167
7168     MovDelay[x][y] = 6 + RND(40);
7169   }
7170   else if (element == EL_PIG)
7171   {
7172     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7173     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7174     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7175     boolean should_turn_left, should_turn_right, should_move_on;
7176     int rnd_value = 24;
7177     int rnd = RND(rnd_value);
7178
7179     should_turn_left = (can_turn_left &&
7180                         (!can_move_on ||
7181                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7182                                                    y + back_dy + left_dy)));
7183     should_turn_right = (can_turn_right &&
7184                          (!can_move_on ||
7185                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7186                                                     y + back_dy + right_dy)));
7187     should_move_on = (can_move_on &&
7188                       (!can_turn_left ||
7189                        !can_turn_right ||
7190                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7191                                                  y + move_dy + left_dy) ||
7192                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7193                                                  y + move_dy + right_dy)));
7194
7195     if (should_turn_left || should_turn_right || should_move_on)
7196     {
7197       if (should_turn_left && should_turn_right && should_move_on)
7198         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7199                         rnd < 2 * rnd_value / 3 ? right_dir :
7200                         old_move_dir);
7201       else if (should_turn_left && should_turn_right)
7202         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7203       else if (should_turn_left && should_move_on)
7204         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7205       else if (should_turn_right && should_move_on)
7206         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7207       else if (should_turn_left)
7208         MovDir[x][y] = left_dir;
7209       else if (should_turn_right)
7210         MovDir[x][y] = right_dir;
7211       else if (should_move_on)
7212         MovDir[x][y] = old_move_dir;
7213     }
7214     else if (can_move_on && rnd > rnd_value / 8)
7215       MovDir[x][y] = old_move_dir;
7216     else if (can_turn_left && can_turn_right)
7217       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7218     else if (can_turn_left && rnd > rnd_value / 8)
7219       MovDir[x][y] = left_dir;
7220     else if (can_turn_right && rnd > rnd_value/8)
7221       MovDir[x][y] = right_dir;
7222     else
7223       MovDir[x][y] = back_dir;
7224
7225     xx = x + move_xy[MovDir[x][y]].dx;
7226     yy = y + move_xy[MovDir[x][y]].dy;
7227
7228     if (!IN_LEV_FIELD(xx, yy) ||
7229         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7230       MovDir[x][y] = old_move_dir;
7231
7232     MovDelay[x][y] = 0;
7233   }
7234   else if (element == EL_DRAGON)
7235   {
7236     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7237     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7238     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7239     int rnd_value = 24;
7240     int rnd = RND(rnd_value);
7241
7242     if (can_move_on && rnd > rnd_value / 8)
7243       MovDir[x][y] = old_move_dir;
7244     else if (can_turn_left && can_turn_right)
7245       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7246     else if (can_turn_left && rnd > rnd_value / 8)
7247       MovDir[x][y] = left_dir;
7248     else if (can_turn_right && rnd > rnd_value / 8)
7249       MovDir[x][y] = right_dir;
7250     else
7251       MovDir[x][y] = back_dir;
7252
7253     xx = x + move_xy[MovDir[x][y]].dx;
7254     yy = y + move_xy[MovDir[x][y]].dy;
7255
7256     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7257       MovDir[x][y] = old_move_dir;
7258
7259     MovDelay[x][y] = 0;
7260   }
7261   else if (element == EL_MOLE)
7262   {
7263     boolean can_move_on =
7264       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7265                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7266                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7267     if (!can_move_on)
7268     {
7269       boolean can_turn_left =
7270         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7271                               IS_AMOEBOID(Tile[left_x][left_y])));
7272
7273       boolean can_turn_right =
7274         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7275                               IS_AMOEBOID(Tile[right_x][right_y])));
7276
7277       if (can_turn_left && can_turn_right)
7278         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7279       else if (can_turn_left)
7280         MovDir[x][y] = left_dir;
7281       else
7282         MovDir[x][y] = right_dir;
7283     }
7284
7285     if (MovDir[x][y] != old_move_dir)
7286       MovDelay[x][y] = 9;
7287   }
7288   else if (element == EL_BALLOON)
7289   {
7290     MovDir[x][y] = game.wind_direction;
7291     MovDelay[x][y] = 0;
7292   }
7293   else if (element == EL_SPRING)
7294   {
7295     if (MovDir[x][y] & MV_HORIZONTAL)
7296     {
7297       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7298           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7299       {
7300         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7301         ResetGfxAnimation(move_x, move_y);
7302         TEST_DrawLevelField(move_x, move_y);
7303
7304         MovDir[x][y] = back_dir;
7305       }
7306       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7307                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7308         MovDir[x][y] = MV_NONE;
7309     }
7310
7311     MovDelay[x][y] = 0;
7312   }
7313   else if (element == EL_ROBOT ||
7314            element == EL_SATELLITE ||
7315            element == EL_PENGUIN ||
7316            element == EL_EMC_ANDROID)
7317   {
7318     int attr_x = -1, attr_y = -1;
7319
7320     if (game.all_players_gone)
7321     {
7322       attr_x = game.exit_x;
7323       attr_y = game.exit_y;
7324     }
7325     else
7326     {
7327       int i;
7328
7329       for (i = 0; i < MAX_PLAYERS; i++)
7330       {
7331         struct PlayerInfo *player = &stored_player[i];
7332         int jx = player->jx, jy = player->jy;
7333
7334         if (!player->active)
7335           continue;
7336
7337         if (attr_x == -1 ||
7338             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7339         {
7340           attr_x = jx;
7341           attr_y = jy;
7342         }
7343       }
7344     }
7345
7346     if (element == EL_ROBOT &&
7347         game.robot_wheel_x >= 0 &&
7348         game.robot_wheel_y >= 0 &&
7349         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7350          game.engine_version < VERSION_IDENT(3,1,0,0)))
7351     {
7352       attr_x = game.robot_wheel_x;
7353       attr_y = game.robot_wheel_y;
7354     }
7355
7356     if (element == EL_PENGUIN)
7357     {
7358       int i;
7359       struct XY *xy = xy_topdown;
7360
7361       for (i = 0; i < NUM_DIRECTIONS; i++)
7362       {
7363         int ex = x + xy[i].x;
7364         int ey = y + xy[i].y;
7365
7366         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7367                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7368                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7369                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7370         {
7371           attr_x = ex;
7372           attr_y = ey;
7373           break;
7374         }
7375       }
7376     }
7377
7378     MovDir[x][y] = MV_NONE;
7379     if (attr_x < x)
7380       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7381     else if (attr_x > x)
7382       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7383     if (attr_y < y)
7384       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7385     else if (attr_y > y)
7386       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7387
7388     if (element == EL_ROBOT)
7389     {
7390       int newx, newy;
7391
7392       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7393         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7394       Moving2Blocked(x, y, &newx, &newy);
7395
7396       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7397         MovDelay[x][y] = 8 + 8 * !RND(3);
7398       else
7399         MovDelay[x][y] = 16;
7400     }
7401     else if (element == EL_PENGUIN)
7402     {
7403       int newx, newy;
7404
7405       MovDelay[x][y] = 1;
7406
7407       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7408       {
7409         boolean first_horiz = RND(2);
7410         int new_move_dir = MovDir[x][y];
7411
7412         MovDir[x][y] =
7413           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7414         Moving2Blocked(x, y, &newx, &newy);
7415
7416         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7417           return;
7418
7419         MovDir[x][y] =
7420           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7421         Moving2Blocked(x, y, &newx, &newy);
7422
7423         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7424           return;
7425
7426         MovDir[x][y] = old_move_dir;
7427         return;
7428       }
7429     }
7430     else if (element == EL_SATELLITE)
7431     {
7432       int newx, newy;
7433
7434       MovDelay[x][y] = 1;
7435
7436       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7437       {
7438         boolean first_horiz = RND(2);
7439         int new_move_dir = MovDir[x][y];
7440
7441         MovDir[x][y] =
7442           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7443         Moving2Blocked(x, y, &newx, &newy);
7444
7445         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7446           return;
7447
7448         MovDir[x][y] =
7449           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7450         Moving2Blocked(x, y, &newx, &newy);
7451
7452         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7453           return;
7454
7455         MovDir[x][y] = old_move_dir;
7456         return;
7457       }
7458     }
7459     else if (element == EL_EMC_ANDROID)
7460     {
7461       static int check_pos[16] =
7462       {
7463         -1,             //  0 => (invalid)
7464         7,              //  1 => MV_LEFT
7465         3,              //  2 => MV_RIGHT
7466         -1,             //  3 => (invalid)
7467         1,              //  4 =>            MV_UP
7468         0,              //  5 => MV_LEFT  | MV_UP
7469         2,              //  6 => MV_RIGHT | MV_UP
7470         -1,             //  7 => (invalid)
7471         5,              //  8 =>            MV_DOWN
7472         6,              //  9 => MV_LEFT  | MV_DOWN
7473         4,              // 10 => MV_RIGHT | MV_DOWN
7474         -1,             // 11 => (invalid)
7475         -1,             // 12 => (invalid)
7476         -1,             // 13 => (invalid)
7477         -1,             // 14 => (invalid)
7478         -1,             // 15 => (invalid)
7479       };
7480       static struct
7481       {
7482         int dx, dy;
7483         int dir;
7484       } check_xy[8] =
7485       {
7486         { -1, -1,       MV_LEFT  | MV_UP   },
7487         {  0, -1,                  MV_UP   },
7488         { +1, -1,       MV_RIGHT | MV_UP   },
7489         { +1,  0,       MV_RIGHT           },
7490         { +1, +1,       MV_RIGHT | MV_DOWN },
7491         {  0, +1,                  MV_DOWN },
7492         { -1, +1,       MV_LEFT  | MV_DOWN },
7493         { -1,  0,       MV_LEFT            },
7494       };
7495       int start_pos, check_order;
7496       boolean can_clone = FALSE;
7497       int i;
7498
7499       // check if there is any free field around current position
7500       for (i = 0; i < 8; i++)
7501       {
7502         int newx = x + check_xy[i].dx;
7503         int newy = y + check_xy[i].dy;
7504
7505         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7506         {
7507           can_clone = TRUE;
7508
7509           break;
7510         }
7511       }
7512
7513       if (can_clone)            // randomly find an element to clone
7514       {
7515         can_clone = FALSE;
7516
7517         start_pos = check_pos[RND(8)];
7518         check_order = (RND(2) ? -1 : +1);
7519
7520         for (i = 0; i < 8; i++)
7521         {
7522           int pos_raw = start_pos + i * check_order;
7523           int pos = (pos_raw + 8) % 8;
7524           int newx = x + check_xy[pos].dx;
7525           int newy = y + check_xy[pos].dy;
7526
7527           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7528           {
7529             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7530             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7531
7532             Store[x][y] = Tile[newx][newy];
7533
7534             can_clone = TRUE;
7535
7536             break;
7537           }
7538         }
7539       }
7540
7541       if (can_clone)            // randomly find a direction to move
7542       {
7543         can_clone = FALSE;
7544
7545         start_pos = check_pos[RND(8)];
7546         check_order = (RND(2) ? -1 : +1);
7547
7548         for (i = 0; i < 8; i++)
7549         {
7550           int pos_raw = start_pos + i * check_order;
7551           int pos = (pos_raw + 8) % 8;
7552           int newx = x + check_xy[pos].dx;
7553           int newy = y + check_xy[pos].dy;
7554           int new_move_dir = check_xy[pos].dir;
7555
7556           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7557           {
7558             MovDir[x][y] = new_move_dir;
7559             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7560
7561             can_clone = TRUE;
7562
7563             break;
7564           }
7565         }
7566       }
7567
7568       if (can_clone)            // cloning and moving successful
7569         return;
7570
7571       // cannot clone -- try to move towards player
7572
7573       start_pos = check_pos[MovDir[x][y] & 0x0f];
7574       check_order = (RND(2) ? -1 : +1);
7575
7576       for (i = 0; i < 3; i++)
7577       {
7578         // first check start_pos, then previous/next or (next/previous) pos
7579         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7580         int pos = (pos_raw + 8) % 8;
7581         int newx = x + check_xy[pos].dx;
7582         int newy = y + check_xy[pos].dy;
7583         int new_move_dir = check_xy[pos].dir;
7584
7585         if (IS_PLAYER(newx, newy))
7586           break;
7587
7588         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7589         {
7590           MovDir[x][y] = new_move_dir;
7591           MovDelay[x][y] = level.android_move_time * 8 + 1;
7592
7593           break;
7594         }
7595       }
7596     }
7597   }
7598   else if (move_pattern == MV_TURNING_LEFT ||
7599            move_pattern == MV_TURNING_RIGHT ||
7600            move_pattern == MV_TURNING_LEFT_RIGHT ||
7601            move_pattern == MV_TURNING_RIGHT_LEFT ||
7602            move_pattern == MV_TURNING_RANDOM ||
7603            move_pattern == MV_ALL_DIRECTIONS)
7604   {
7605     boolean can_turn_left =
7606       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7607     boolean can_turn_right =
7608       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7609
7610     if (element_info[element].move_stepsize == 0)       // "not moving"
7611       return;
7612
7613     if (move_pattern == MV_TURNING_LEFT)
7614       MovDir[x][y] = left_dir;
7615     else if (move_pattern == MV_TURNING_RIGHT)
7616       MovDir[x][y] = right_dir;
7617     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7618       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7619     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7620       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7621     else if (move_pattern == MV_TURNING_RANDOM)
7622       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7623                       can_turn_right && !can_turn_left ? right_dir :
7624                       RND(2) ? left_dir : right_dir);
7625     else if (can_turn_left && can_turn_right)
7626       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7627     else if (can_turn_left)
7628       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7629     else if (can_turn_right)
7630       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7631     else
7632       MovDir[x][y] = back_dir;
7633
7634     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7635   }
7636   else if (move_pattern == MV_HORIZONTAL ||
7637            move_pattern == MV_VERTICAL)
7638   {
7639     if (move_pattern & old_move_dir)
7640       MovDir[x][y] = back_dir;
7641     else if (move_pattern == MV_HORIZONTAL)
7642       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7643     else if (move_pattern == MV_VERTICAL)
7644       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7645
7646     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7647   }
7648   else if (move_pattern & MV_ANY_DIRECTION)
7649   {
7650     MovDir[x][y] = move_pattern;
7651     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7652   }
7653   else if (move_pattern & MV_WIND_DIRECTION)
7654   {
7655     MovDir[x][y] = game.wind_direction;
7656     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7657   }
7658   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7659   {
7660     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7661       MovDir[x][y] = left_dir;
7662     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7663       MovDir[x][y] = right_dir;
7664
7665     if (MovDir[x][y] != old_move_dir)
7666       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7667   }
7668   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7669   {
7670     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7671       MovDir[x][y] = right_dir;
7672     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7673       MovDir[x][y] = left_dir;
7674
7675     if (MovDir[x][y] != old_move_dir)
7676       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7677   }
7678   else if (move_pattern == MV_TOWARDS_PLAYER ||
7679            move_pattern == MV_AWAY_FROM_PLAYER)
7680   {
7681     int attr_x = -1, attr_y = -1;
7682     int newx, newy;
7683     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7684
7685     if (game.all_players_gone)
7686     {
7687       attr_x = game.exit_x;
7688       attr_y = game.exit_y;
7689     }
7690     else
7691     {
7692       int i;
7693
7694       for (i = 0; i < MAX_PLAYERS; i++)
7695       {
7696         struct PlayerInfo *player = &stored_player[i];
7697         int jx = player->jx, jy = player->jy;
7698
7699         if (!player->active)
7700           continue;
7701
7702         if (attr_x == -1 ||
7703             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7704         {
7705           attr_x = jx;
7706           attr_y = jy;
7707         }
7708       }
7709     }
7710
7711     MovDir[x][y] = MV_NONE;
7712     if (attr_x < x)
7713       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7714     else if (attr_x > x)
7715       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7716     if (attr_y < y)
7717       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7718     else if (attr_y > y)
7719       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7720
7721     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7722
7723     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7724     {
7725       boolean first_horiz = RND(2);
7726       int new_move_dir = MovDir[x][y];
7727
7728       if (element_info[element].move_stepsize == 0)     // "not moving"
7729       {
7730         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7731         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7732
7733         return;
7734       }
7735
7736       MovDir[x][y] =
7737         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7738       Moving2Blocked(x, y, &newx, &newy);
7739
7740       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7741         return;
7742
7743       MovDir[x][y] =
7744         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7745       Moving2Blocked(x, y, &newx, &newy);
7746
7747       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7748         return;
7749
7750       MovDir[x][y] = old_move_dir;
7751     }
7752   }
7753   else if (move_pattern == MV_WHEN_PUSHED ||
7754            move_pattern == MV_WHEN_DROPPED)
7755   {
7756     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7757       MovDir[x][y] = MV_NONE;
7758
7759     MovDelay[x][y] = 0;
7760   }
7761   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7762   {
7763     struct XY *test_xy = xy_topdown;
7764     static int test_dir[4] =
7765     {
7766       MV_UP,
7767       MV_LEFT,
7768       MV_RIGHT,
7769       MV_DOWN
7770     };
7771     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7772     int move_preference = -1000000;     // start with very low preference
7773     int new_move_dir = MV_NONE;
7774     int start_test = RND(4);
7775     int i;
7776
7777     for (i = 0; i < NUM_DIRECTIONS; i++)
7778     {
7779       int j = (start_test + i) % 4;
7780       int move_dir = test_dir[j];
7781       int move_dir_preference;
7782
7783       xx = x + test_xy[j].x;
7784       yy = y + test_xy[j].y;
7785
7786       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7787           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7788       {
7789         new_move_dir = move_dir;
7790
7791         break;
7792       }
7793
7794       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7795         continue;
7796
7797       move_dir_preference = -1 * RunnerVisit[xx][yy];
7798       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7799         move_dir_preference = PlayerVisit[xx][yy];
7800
7801       if (move_dir_preference > move_preference)
7802       {
7803         // prefer field that has not been visited for the longest time
7804         move_preference = move_dir_preference;
7805         new_move_dir = move_dir;
7806       }
7807       else if (move_dir_preference == move_preference &&
7808                move_dir == old_move_dir)
7809       {
7810         // prefer last direction when all directions are preferred equally
7811         move_preference = move_dir_preference;
7812         new_move_dir = move_dir;
7813       }
7814     }
7815
7816     MovDir[x][y] = new_move_dir;
7817     if (old_move_dir != new_move_dir)
7818       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7819   }
7820 }
7821
7822 static void TurnRound(int x, int y)
7823 {
7824   int direction = MovDir[x][y];
7825
7826   TurnRoundExt(x, y);
7827
7828   GfxDir[x][y] = MovDir[x][y];
7829
7830   if (direction != MovDir[x][y])
7831     GfxFrame[x][y] = 0;
7832
7833   if (MovDelay[x][y])
7834     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7835
7836   ResetGfxFrame(x, y);
7837 }
7838
7839 static boolean JustBeingPushed(int x, int y)
7840 {
7841   int i;
7842
7843   for (i = 0; i < MAX_PLAYERS; i++)
7844   {
7845     struct PlayerInfo *player = &stored_player[i];
7846
7847     if (player->active && player->is_pushing && player->MovPos)
7848     {
7849       int next_jx = player->jx + (player->jx - player->last_jx);
7850       int next_jy = player->jy + (player->jy - player->last_jy);
7851
7852       if (x == next_jx && y == next_jy)
7853         return TRUE;
7854     }
7855   }
7856
7857   return FALSE;
7858 }
7859
7860 static void StartMoving(int x, int y)
7861 {
7862   boolean started_moving = FALSE;       // some elements can fall _and_ move
7863   int element = Tile[x][y];
7864
7865   if (Stop[x][y])
7866     return;
7867
7868   if (MovDelay[x][y] == 0)
7869     GfxAction[x][y] = ACTION_DEFAULT;
7870
7871   if (CAN_FALL(element) && y < lev_fieldy - 1)
7872   {
7873     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7874         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7875       if (JustBeingPushed(x, y))
7876         return;
7877
7878     if (element == EL_QUICKSAND_FULL)
7879     {
7880       if (IS_FREE(x, y + 1))
7881       {
7882         InitMovingField(x, y, MV_DOWN);
7883         started_moving = TRUE;
7884
7885         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7886 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7887         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7888           Store[x][y] = EL_ROCK;
7889 #else
7890         Store[x][y] = EL_ROCK;
7891 #endif
7892
7893         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7894       }
7895       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7896       {
7897         if (!MovDelay[x][y])
7898         {
7899           MovDelay[x][y] = TILEY + 1;
7900
7901           ResetGfxAnimation(x, y);
7902           ResetGfxAnimation(x, y + 1);
7903         }
7904
7905         if (MovDelay[x][y])
7906         {
7907           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7908           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7909
7910           MovDelay[x][y]--;
7911           if (MovDelay[x][y])
7912             return;
7913         }
7914
7915         Tile[x][y] = EL_QUICKSAND_EMPTY;
7916         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7917         Store[x][y + 1] = Store[x][y];
7918         Store[x][y] = 0;
7919
7920         PlayLevelSoundAction(x, y, ACTION_FILLING);
7921       }
7922       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7923       {
7924         if (!MovDelay[x][y])
7925         {
7926           MovDelay[x][y] = TILEY + 1;
7927
7928           ResetGfxAnimation(x, y);
7929           ResetGfxAnimation(x, y + 1);
7930         }
7931
7932         if (MovDelay[x][y])
7933         {
7934           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7935           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7936
7937           MovDelay[x][y]--;
7938           if (MovDelay[x][y])
7939             return;
7940         }
7941
7942         Tile[x][y] = EL_QUICKSAND_EMPTY;
7943         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7944         Store[x][y + 1] = Store[x][y];
7945         Store[x][y] = 0;
7946
7947         PlayLevelSoundAction(x, y, ACTION_FILLING);
7948       }
7949     }
7950     else if (element == EL_QUICKSAND_FAST_FULL)
7951     {
7952       if (IS_FREE(x, y + 1))
7953       {
7954         InitMovingField(x, y, MV_DOWN);
7955         started_moving = TRUE;
7956
7957         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7958 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7959         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7960           Store[x][y] = EL_ROCK;
7961 #else
7962         Store[x][y] = EL_ROCK;
7963 #endif
7964
7965         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7966       }
7967       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7968       {
7969         if (!MovDelay[x][y])
7970         {
7971           MovDelay[x][y] = TILEY + 1;
7972
7973           ResetGfxAnimation(x, y);
7974           ResetGfxAnimation(x, y + 1);
7975         }
7976
7977         if (MovDelay[x][y])
7978         {
7979           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7980           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7981
7982           MovDelay[x][y]--;
7983           if (MovDelay[x][y])
7984             return;
7985         }
7986
7987         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7988         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7989         Store[x][y + 1] = Store[x][y];
7990         Store[x][y] = 0;
7991
7992         PlayLevelSoundAction(x, y, ACTION_FILLING);
7993       }
7994       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7995       {
7996         if (!MovDelay[x][y])
7997         {
7998           MovDelay[x][y] = TILEY + 1;
7999
8000           ResetGfxAnimation(x, y);
8001           ResetGfxAnimation(x, y + 1);
8002         }
8003
8004         if (MovDelay[x][y])
8005         {
8006           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8007           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8008
8009           MovDelay[x][y]--;
8010           if (MovDelay[x][y])
8011             return;
8012         }
8013
8014         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8015         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8016         Store[x][y + 1] = Store[x][y];
8017         Store[x][y] = 0;
8018
8019         PlayLevelSoundAction(x, y, ACTION_FILLING);
8020       }
8021     }
8022     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8023              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8024     {
8025       InitMovingField(x, y, MV_DOWN);
8026       started_moving = TRUE;
8027
8028       Tile[x][y] = EL_QUICKSAND_FILLING;
8029       Store[x][y] = element;
8030
8031       PlayLevelSoundAction(x, y, ACTION_FILLING);
8032     }
8033     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8034              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8035     {
8036       InitMovingField(x, y, MV_DOWN);
8037       started_moving = TRUE;
8038
8039       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
8040       Store[x][y] = element;
8041
8042       PlayLevelSoundAction(x, y, ACTION_FILLING);
8043     }
8044     else if (element == EL_MAGIC_WALL_FULL)
8045     {
8046       if (IS_FREE(x, y + 1))
8047       {
8048         InitMovingField(x, y, MV_DOWN);
8049         started_moving = TRUE;
8050
8051         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
8052         Store[x][y] = EL_CHANGED(Store[x][y]);
8053       }
8054       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8055       {
8056         if (!MovDelay[x][y])
8057           MovDelay[x][y] = TILEY / 4 + 1;
8058
8059         if (MovDelay[x][y])
8060         {
8061           MovDelay[x][y]--;
8062           if (MovDelay[x][y])
8063             return;
8064         }
8065
8066         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8067         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8068         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8069         Store[x][y] = 0;
8070       }
8071     }
8072     else if (element == EL_BD_MAGIC_WALL_FULL)
8073     {
8074       if (IS_FREE(x, y + 1))
8075       {
8076         InitMovingField(x, y, MV_DOWN);
8077         started_moving = TRUE;
8078
8079         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8080         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8081       }
8082       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8083       {
8084         if (!MovDelay[x][y])
8085           MovDelay[x][y] = TILEY / 4 + 1;
8086
8087         if (MovDelay[x][y])
8088         {
8089           MovDelay[x][y]--;
8090           if (MovDelay[x][y])
8091             return;
8092         }
8093
8094         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8095         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8096         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8097         Store[x][y] = 0;
8098       }
8099     }
8100     else if (element == EL_DC_MAGIC_WALL_FULL)
8101     {
8102       if (IS_FREE(x, y + 1))
8103       {
8104         InitMovingField(x, y, MV_DOWN);
8105         started_moving = TRUE;
8106
8107         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8108         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8109       }
8110       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8111       {
8112         if (!MovDelay[x][y])
8113           MovDelay[x][y] = TILEY / 4 + 1;
8114
8115         if (MovDelay[x][y])
8116         {
8117           MovDelay[x][y]--;
8118           if (MovDelay[x][y])
8119             return;
8120         }
8121
8122         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8123         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8124         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8125         Store[x][y] = 0;
8126       }
8127     }
8128     else if ((CAN_PASS_MAGIC_WALL(element) &&
8129               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8130                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8131              (CAN_PASS_DC_MAGIC_WALL(element) &&
8132               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8133
8134     {
8135       InitMovingField(x, y, MV_DOWN);
8136       started_moving = TRUE;
8137
8138       Tile[x][y] =
8139         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8140          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8141          EL_DC_MAGIC_WALL_FILLING);
8142       Store[x][y] = element;
8143     }
8144     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8145     {
8146       SplashAcid(x, y + 1);
8147
8148       InitMovingField(x, y, MV_DOWN);
8149       started_moving = TRUE;
8150
8151       Store[x][y] = EL_ACID;
8152     }
8153     else if (
8154              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8155               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8156              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8157               CAN_FALL(element) && WasJustFalling[x][y] &&
8158               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8159
8160              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8161               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8162               (Tile[x][y + 1] == EL_BLOCKED)))
8163     {
8164       /* this is needed for a special case not covered by calling "Impact()"
8165          from "ContinueMoving()": if an element moves to a tile directly below
8166          another element which was just falling on that tile (which was empty
8167          in the previous frame), the falling element above would just stop
8168          instead of smashing the element below (in previous version, the above
8169          element was just checked for "moving" instead of "falling", resulting
8170          in incorrect smashes caused by horizontal movement of the above
8171          element; also, the case of the player being the element to smash was
8172          simply not covered here... :-/ ) */
8173
8174       CheckCollision[x][y] = 0;
8175       CheckImpact[x][y] = 0;
8176
8177       Impact(x, y);
8178     }
8179     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8180     {
8181       if (MovDir[x][y] == MV_NONE)
8182       {
8183         InitMovingField(x, y, MV_DOWN);
8184         started_moving = TRUE;
8185       }
8186     }
8187     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8188     {
8189       if (WasJustFalling[x][y]) // prevent animation from being restarted
8190         MovDir[x][y] = MV_DOWN;
8191
8192       InitMovingField(x, y, MV_DOWN);
8193       started_moving = TRUE;
8194     }
8195     else if (element == EL_AMOEBA_DROP)
8196     {
8197       Tile[x][y] = EL_AMOEBA_GROWING;
8198       Store[x][y] = EL_AMOEBA_WET;
8199     }
8200     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8201               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8202              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8203              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8204     {
8205       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8206                                 (IS_FREE(x - 1, y + 1) ||
8207                                  Tile[x - 1][y + 1] == EL_ACID));
8208       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8209                                 (IS_FREE(x + 1, y + 1) ||
8210                                  Tile[x + 1][y + 1] == EL_ACID));
8211       boolean can_fall_any  = (can_fall_left || can_fall_right);
8212       boolean can_fall_both = (can_fall_left && can_fall_right);
8213       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8214
8215       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8216       {
8217         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8218           can_fall_right = FALSE;
8219         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8220           can_fall_left = FALSE;
8221         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8222           can_fall_right = FALSE;
8223         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8224           can_fall_left = FALSE;
8225
8226         can_fall_any  = (can_fall_left || can_fall_right);
8227         can_fall_both = FALSE;
8228       }
8229
8230       if (can_fall_both)
8231       {
8232         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8233           can_fall_right = FALSE;       // slip down on left side
8234         else
8235           can_fall_left = !(can_fall_right = RND(2));
8236
8237         can_fall_both = FALSE;
8238       }
8239
8240       if (can_fall_any)
8241       {
8242         // if not determined otherwise, prefer left side for slipping down
8243         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8244         started_moving = TRUE;
8245       }
8246     }
8247     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8248     {
8249       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8250       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8251       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8252       int belt_dir = game.belt_dir[belt_nr];
8253
8254       if ((belt_dir == MV_LEFT  && left_is_free) ||
8255           (belt_dir == MV_RIGHT && right_is_free))
8256       {
8257         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8258
8259         InitMovingField(x, y, belt_dir);
8260         started_moving = TRUE;
8261
8262         Pushed[x][y] = TRUE;
8263         Pushed[nextx][y] = TRUE;
8264
8265         GfxAction[x][y] = ACTION_DEFAULT;
8266       }
8267       else
8268       {
8269         MovDir[x][y] = 0;       // if element was moving, stop it
8270       }
8271     }
8272   }
8273
8274   // not "else if" because of elements that can fall and move (EL_SPRING)
8275   if (CAN_MOVE(element) && !started_moving)
8276   {
8277     int move_pattern = element_info[element].move_pattern;
8278     int newx, newy;
8279
8280     Moving2Blocked(x, y, &newx, &newy);
8281
8282     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8283       return;
8284
8285     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8286         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8287     {
8288       WasJustMoving[x][y] = 0;
8289       CheckCollision[x][y] = 0;
8290
8291       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8292
8293       if (Tile[x][y] != element)        // element has changed
8294         return;
8295     }
8296
8297     if (!MovDelay[x][y])        // start new movement phase
8298     {
8299       // all objects that can change their move direction after each step
8300       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8301
8302       if (element != EL_YAMYAM &&
8303           element != EL_DARK_YAMYAM &&
8304           element != EL_PACMAN &&
8305           !(move_pattern & MV_ANY_DIRECTION) &&
8306           move_pattern != MV_TURNING_LEFT &&
8307           move_pattern != MV_TURNING_RIGHT &&
8308           move_pattern != MV_TURNING_LEFT_RIGHT &&
8309           move_pattern != MV_TURNING_RIGHT_LEFT &&
8310           move_pattern != MV_TURNING_RANDOM)
8311       {
8312         TurnRound(x, y);
8313
8314         if (MovDelay[x][y] && (element == EL_BUG ||
8315                                element == EL_SPACESHIP ||
8316                                element == EL_SP_SNIKSNAK ||
8317                                element == EL_SP_ELECTRON ||
8318                                element == EL_MOLE))
8319           TEST_DrawLevelField(x, y);
8320       }
8321     }
8322
8323     if (MovDelay[x][y])         // wait some time before next movement
8324     {
8325       MovDelay[x][y]--;
8326
8327       if (element == EL_ROBOT ||
8328           element == EL_YAMYAM ||
8329           element == EL_DARK_YAMYAM)
8330       {
8331         DrawLevelElementAnimationIfNeeded(x, y, element);
8332         PlayLevelSoundAction(x, y, ACTION_WAITING);
8333       }
8334       else if (element == EL_SP_ELECTRON)
8335         DrawLevelElementAnimationIfNeeded(x, y, element);
8336       else if (element == EL_DRAGON)
8337       {
8338         int i;
8339         int dir = MovDir[x][y];
8340         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8341         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8342         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8343                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8344                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8345                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8346         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8347
8348         GfxAction[x][y] = ACTION_ATTACKING;
8349
8350         if (IS_PLAYER(x, y))
8351           DrawPlayerField(x, y);
8352         else
8353           TEST_DrawLevelField(x, y);
8354
8355         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8356
8357         for (i = 1; i <= 3; i++)
8358         {
8359           int xx = x + i * dx;
8360           int yy = y + i * dy;
8361           int sx = SCREENX(xx);
8362           int sy = SCREENY(yy);
8363           int flame_graphic = graphic + (i - 1);
8364
8365           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8366             break;
8367
8368           if (MovDelay[x][y])
8369           {
8370             int flamed = MovingOrBlocked2Element(xx, yy);
8371
8372             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8373               Bang(xx, yy);
8374             else
8375               RemoveMovingField(xx, yy);
8376
8377             ChangeDelay[xx][yy] = 0;
8378
8379             Tile[xx][yy] = EL_FLAMES;
8380
8381             if (IN_SCR_FIELD(sx, sy))
8382             {
8383               TEST_DrawLevelFieldCrumbled(xx, yy);
8384               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8385             }
8386           }
8387           else
8388           {
8389             if (Tile[xx][yy] == EL_FLAMES)
8390               Tile[xx][yy] = EL_EMPTY;
8391             TEST_DrawLevelField(xx, yy);
8392           }
8393         }
8394       }
8395
8396       if (MovDelay[x][y])       // element still has to wait some time
8397       {
8398         PlayLevelSoundAction(x, y, ACTION_WAITING);
8399
8400         return;
8401       }
8402     }
8403
8404     // now make next step
8405
8406     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8407
8408     if (DONT_COLLIDE_WITH(element) &&
8409         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8410         !PLAYER_ENEMY_PROTECTED(newx, newy))
8411     {
8412       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8413
8414       return;
8415     }
8416
8417     else if (CAN_MOVE_INTO_ACID(element) &&
8418              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8419              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8420              (MovDir[x][y] == MV_DOWN ||
8421               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8422     {
8423       SplashAcid(newx, newy);
8424       Store[x][y] = EL_ACID;
8425     }
8426     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8427     {
8428       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8429           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8430           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8431           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8432       {
8433         RemoveField(x, y);
8434         TEST_DrawLevelField(x, y);
8435
8436         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8437         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8438           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8439
8440         game.friends_still_needed--;
8441         if (!game.friends_still_needed &&
8442             !game.GameOver &&
8443             game.all_players_gone)
8444           LevelSolved();
8445
8446         return;
8447       }
8448       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8449       {
8450         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8451           TEST_DrawLevelField(newx, newy);
8452         else
8453           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8454       }
8455       else if (!IS_FREE(newx, newy))
8456       {
8457         GfxAction[x][y] = ACTION_WAITING;
8458
8459         if (IS_PLAYER(x, y))
8460           DrawPlayerField(x, y);
8461         else
8462           TEST_DrawLevelField(x, y);
8463
8464         return;
8465       }
8466     }
8467     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8468     {
8469       if (IS_FOOD_PIG(Tile[newx][newy]))
8470       {
8471         if (IS_MOVING(newx, newy))
8472           RemoveMovingField(newx, newy);
8473         else
8474         {
8475           Tile[newx][newy] = EL_EMPTY;
8476           TEST_DrawLevelField(newx, newy);
8477         }
8478
8479         PlayLevelSound(x, y, SND_PIG_DIGGING);
8480       }
8481       else if (!IS_FREE(newx, newy))
8482       {
8483         if (IS_PLAYER(x, y))
8484           DrawPlayerField(x, y);
8485         else
8486           TEST_DrawLevelField(x, y);
8487
8488         return;
8489       }
8490     }
8491     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8492     {
8493       if (Store[x][y] != EL_EMPTY)
8494       {
8495         boolean can_clone = FALSE;
8496         int xx, yy;
8497
8498         // check if element to clone is still there
8499         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8500         {
8501           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8502           {
8503             can_clone = TRUE;
8504
8505             break;
8506           }
8507         }
8508
8509         // cannot clone or target field not free anymore -- do not clone
8510         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8511           Store[x][y] = EL_EMPTY;
8512       }
8513
8514       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8515       {
8516         if (IS_MV_DIAGONAL(MovDir[x][y]))
8517         {
8518           int diagonal_move_dir = MovDir[x][y];
8519           int stored = Store[x][y];
8520           int change_delay = 8;
8521           int graphic;
8522
8523           // android is moving diagonally
8524
8525           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8526
8527           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8528           GfxElement[x][y] = EL_EMC_ANDROID;
8529           GfxAction[x][y] = ACTION_SHRINKING;
8530           GfxDir[x][y] = diagonal_move_dir;
8531           ChangeDelay[x][y] = change_delay;
8532
8533           if (Store[x][y] == EL_EMPTY)
8534             Store[x][y] = GfxElementEmpty[x][y];
8535
8536           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8537                                    GfxDir[x][y]);
8538
8539           DrawLevelGraphicAnimation(x, y, graphic);
8540           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8541
8542           if (Tile[newx][newy] == EL_ACID)
8543           {
8544             SplashAcid(newx, newy);
8545
8546             return;
8547           }
8548
8549           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8550
8551           Store[newx][newy] = EL_EMC_ANDROID;
8552           GfxElement[newx][newy] = EL_EMC_ANDROID;
8553           GfxAction[newx][newy] = ACTION_GROWING;
8554           GfxDir[newx][newy] = diagonal_move_dir;
8555           ChangeDelay[newx][newy] = change_delay;
8556
8557           graphic = el_act_dir2img(GfxElement[newx][newy],
8558                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8559
8560           DrawLevelGraphicAnimation(newx, newy, graphic);
8561           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8562
8563           return;
8564         }
8565         else
8566         {
8567           Tile[newx][newy] = EL_EMPTY;
8568           TEST_DrawLevelField(newx, newy);
8569
8570           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8571         }
8572       }
8573       else if (!IS_FREE(newx, newy))
8574       {
8575         return;
8576       }
8577     }
8578     else if (IS_CUSTOM_ELEMENT(element) &&
8579              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8580     {
8581       if (!DigFieldByCE(newx, newy, element))
8582         return;
8583
8584       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8585       {
8586         RunnerVisit[x][y] = FrameCounter;
8587         PlayerVisit[x][y] /= 8;         // expire player visit path
8588       }
8589     }
8590     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8591     {
8592       if (!IS_FREE(newx, newy))
8593       {
8594         if (IS_PLAYER(x, y))
8595           DrawPlayerField(x, y);
8596         else
8597           TEST_DrawLevelField(x, y);
8598
8599         return;
8600       }
8601       else
8602       {
8603         boolean wanna_flame = !RND(10);
8604         int dx = newx - x, dy = newy - y;
8605         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8606         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8607         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8608                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8609         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8610                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8611
8612         if ((wanna_flame ||
8613              IS_CLASSIC_ENEMY(element1) ||
8614              IS_CLASSIC_ENEMY(element2)) &&
8615             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8616             element1 != EL_FLAMES && element2 != EL_FLAMES)
8617         {
8618           ResetGfxAnimation(x, y);
8619           GfxAction[x][y] = ACTION_ATTACKING;
8620
8621           if (IS_PLAYER(x, y))
8622             DrawPlayerField(x, y);
8623           else
8624             TEST_DrawLevelField(x, y);
8625
8626           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8627
8628           MovDelay[x][y] = 50;
8629
8630           Tile[newx][newy] = EL_FLAMES;
8631           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8632             Tile[newx1][newy1] = EL_FLAMES;
8633           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8634             Tile[newx2][newy2] = EL_FLAMES;
8635
8636           return;
8637         }
8638       }
8639     }
8640     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8641              Tile[newx][newy] == EL_DIAMOND)
8642     {
8643       if (IS_MOVING(newx, newy))
8644         RemoveMovingField(newx, newy);
8645       else
8646       {
8647         Tile[newx][newy] = EL_EMPTY;
8648         TEST_DrawLevelField(newx, newy);
8649       }
8650
8651       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8652     }
8653     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8654              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8655     {
8656       if (AmoebaNr[newx][newy])
8657       {
8658         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8659         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8660             Tile[newx][newy] == EL_BD_AMOEBA)
8661           AmoebaCnt[AmoebaNr[newx][newy]]--;
8662       }
8663
8664       if (IS_MOVING(newx, newy))
8665       {
8666         RemoveMovingField(newx, newy);
8667       }
8668       else
8669       {
8670         Tile[newx][newy] = EL_EMPTY;
8671         TEST_DrawLevelField(newx, newy);
8672       }
8673
8674       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8675     }
8676     else if ((element == EL_PACMAN || element == EL_MOLE)
8677              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8678     {
8679       if (AmoebaNr[newx][newy])
8680       {
8681         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8682         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8683             Tile[newx][newy] == EL_BD_AMOEBA)
8684           AmoebaCnt[AmoebaNr[newx][newy]]--;
8685       }
8686
8687       if (element == EL_MOLE)
8688       {
8689         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8690         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8691
8692         ResetGfxAnimation(x, y);
8693         GfxAction[x][y] = ACTION_DIGGING;
8694         TEST_DrawLevelField(x, y);
8695
8696         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8697
8698         return;                         // wait for shrinking amoeba
8699       }
8700       else      // element == EL_PACMAN
8701       {
8702         Tile[newx][newy] = EL_EMPTY;
8703         TEST_DrawLevelField(newx, newy);
8704         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8705       }
8706     }
8707     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8708              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8709               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8710     {
8711       // wait for shrinking amoeba to completely disappear
8712       return;
8713     }
8714     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8715     {
8716       // object was running against a wall
8717
8718       TurnRound(x, y);
8719
8720       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8721         DrawLevelElementAnimation(x, y, element);
8722
8723       if (DONT_TOUCH(element))
8724         TestIfBadThingTouchesPlayer(x, y);
8725
8726       return;
8727     }
8728
8729     InitMovingField(x, y, MovDir[x][y]);
8730
8731     PlayLevelSoundAction(x, y, ACTION_MOVING);
8732   }
8733
8734   if (MovDir[x][y])
8735     ContinueMoving(x, y);
8736 }
8737
8738 void ContinueMoving(int x, int y)
8739 {
8740   int element = Tile[x][y];
8741   struct ElementInfo *ei = &element_info[element];
8742   int direction = MovDir[x][y];
8743   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8744   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8745   int newx = x + dx, newy = y + dy;
8746   int stored = Store[x][y];
8747   int stored_new = Store[newx][newy];
8748   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8749   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8750   boolean last_line = (newy == lev_fieldy - 1);
8751   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8752
8753   if (pushed_by_player)         // special case: moving object pushed by player
8754   {
8755     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8756   }
8757   else if (use_step_delay)      // special case: moving object has step delay
8758   {
8759     if (!MovDelay[x][y])
8760       MovPos[x][y] += getElementMoveStepsize(x, y);
8761
8762     if (MovDelay[x][y])
8763       MovDelay[x][y]--;
8764     else
8765       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8766
8767     if (MovDelay[x][y])
8768     {
8769       TEST_DrawLevelField(x, y);
8770
8771       return;   // element is still waiting
8772     }
8773   }
8774   else                          // normal case: generically moving object
8775   {
8776     MovPos[x][y] += getElementMoveStepsize(x, y);
8777   }
8778
8779   if (ABS(MovPos[x][y]) < TILEX)
8780   {
8781     TEST_DrawLevelField(x, y);
8782
8783     return;     // element is still moving
8784   }
8785
8786   // element reached destination field
8787
8788   Tile[x][y] = EL_EMPTY;
8789   Tile[newx][newy] = element;
8790   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8791
8792   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8793   {
8794     element = Tile[newx][newy] = EL_ACID;
8795   }
8796   else if (element == EL_MOLE)
8797   {
8798     Tile[x][y] = EL_SAND;
8799
8800     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8801   }
8802   else if (element == EL_QUICKSAND_FILLING)
8803   {
8804     element = Tile[newx][newy] = get_next_element(element);
8805     Store[newx][newy] = Store[x][y];
8806   }
8807   else if (element == EL_QUICKSAND_EMPTYING)
8808   {
8809     Tile[x][y] = get_next_element(element);
8810     element = Tile[newx][newy] = Store[x][y];
8811   }
8812   else if (element == EL_QUICKSAND_FAST_FILLING)
8813   {
8814     element = Tile[newx][newy] = get_next_element(element);
8815     Store[newx][newy] = Store[x][y];
8816   }
8817   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8818   {
8819     Tile[x][y] = get_next_element(element);
8820     element = Tile[newx][newy] = Store[x][y];
8821   }
8822   else if (element == EL_MAGIC_WALL_FILLING)
8823   {
8824     element = Tile[newx][newy] = get_next_element(element);
8825     if (!game.magic_wall_active)
8826       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8827     Store[newx][newy] = Store[x][y];
8828   }
8829   else if (element == EL_MAGIC_WALL_EMPTYING)
8830   {
8831     Tile[x][y] = get_next_element(element);
8832     if (!game.magic_wall_active)
8833       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8834     element = Tile[newx][newy] = Store[x][y];
8835
8836     InitField(newx, newy, FALSE);
8837   }
8838   else if (element == EL_BD_MAGIC_WALL_FILLING)
8839   {
8840     element = Tile[newx][newy] = get_next_element(element);
8841     if (!game.magic_wall_active)
8842       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8843     Store[newx][newy] = Store[x][y];
8844   }
8845   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8846   {
8847     Tile[x][y] = get_next_element(element);
8848     if (!game.magic_wall_active)
8849       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8850     element = Tile[newx][newy] = Store[x][y];
8851
8852     InitField(newx, newy, FALSE);
8853   }
8854   else if (element == EL_DC_MAGIC_WALL_FILLING)
8855   {
8856     element = Tile[newx][newy] = get_next_element(element);
8857     if (!game.magic_wall_active)
8858       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8859     Store[newx][newy] = Store[x][y];
8860   }
8861   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8862   {
8863     Tile[x][y] = get_next_element(element);
8864     if (!game.magic_wall_active)
8865       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8866     element = Tile[newx][newy] = Store[x][y];
8867
8868     InitField(newx, newy, FALSE);
8869   }
8870   else if (element == EL_AMOEBA_DROPPING)
8871   {
8872     Tile[x][y] = get_next_element(element);
8873     element = Tile[newx][newy] = Store[x][y];
8874   }
8875   else if (element == EL_SOKOBAN_OBJECT)
8876   {
8877     if (Back[x][y])
8878       Tile[x][y] = Back[x][y];
8879
8880     if (Back[newx][newy])
8881       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8882
8883     Back[x][y] = Back[newx][newy] = 0;
8884   }
8885
8886   Store[x][y] = EL_EMPTY;
8887   MovPos[x][y] = 0;
8888   MovDir[x][y] = 0;
8889   MovDelay[x][y] = 0;
8890
8891   MovDelay[newx][newy] = 0;
8892
8893   if (CAN_CHANGE_OR_HAS_ACTION(element))
8894   {
8895     // copy element change control values to new field
8896     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8897     ChangePage[newx][newy]  = ChangePage[x][y];
8898     ChangeCount[newx][newy] = ChangeCount[x][y];
8899     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8900   }
8901
8902   CustomValue[newx][newy] = CustomValue[x][y];
8903
8904   ChangeDelay[x][y] = 0;
8905   ChangePage[x][y] = -1;
8906   ChangeCount[x][y] = 0;
8907   ChangeEvent[x][y] = -1;
8908
8909   CustomValue[x][y] = 0;
8910
8911   // copy animation control values to new field
8912   GfxFrame[newx][newy]  = GfxFrame[x][y];
8913   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8914   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8915   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8916
8917   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8918
8919   // some elements can leave other elements behind after moving
8920   if (ei->move_leave_element != EL_EMPTY &&
8921       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8922       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8923   {
8924     int move_leave_element = ei->move_leave_element;
8925
8926     // this makes it possible to leave the removed element again
8927     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8928       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8929
8930     Tile[x][y] = move_leave_element;
8931
8932     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8933       MovDir[x][y] = direction;
8934
8935     InitField(x, y, FALSE);
8936
8937     if (GFX_CRUMBLED(Tile[x][y]))
8938       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8939
8940     if (IS_PLAYER_ELEMENT(move_leave_element))
8941       RelocatePlayer(x, y, move_leave_element);
8942   }
8943
8944   // do this after checking for left-behind element
8945   ResetGfxAnimation(x, y);      // reset animation values for old field
8946
8947   if (!CAN_MOVE(element) ||
8948       (CAN_FALL(element) && direction == MV_DOWN &&
8949        (element == EL_SPRING ||
8950         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8951         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8952     GfxDir[x][y] = MovDir[newx][newy] = 0;
8953
8954   TEST_DrawLevelField(x, y);
8955   TEST_DrawLevelField(newx, newy);
8956
8957   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8958
8959   // prevent pushed element from moving on in pushed direction
8960   if (pushed_by_player && CAN_MOVE(element) &&
8961       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8962       !(element_info[element].move_pattern & direction))
8963     TurnRound(newx, newy);
8964
8965   // prevent elements on conveyor belt from moving on in last direction
8966   if (pushed_by_conveyor && CAN_FALL(element) &&
8967       direction & MV_HORIZONTAL)
8968     MovDir[newx][newy] = 0;
8969
8970   if (!pushed_by_player)
8971   {
8972     int nextx = newx + dx, nexty = newy + dy;
8973     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8974
8975     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8976
8977     if (CAN_FALL(element) && direction == MV_DOWN)
8978       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8979
8980     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8981       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8982
8983     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8984       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8985   }
8986
8987   if (DONT_TOUCH(element))      // object may be nasty to player or others
8988   {
8989     TestIfBadThingTouchesPlayer(newx, newy);
8990     TestIfBadThingTouchesFriend(newx, newy);
8991
8992     if (!IS_CUSTOM_ELEMENT(element))
8993       TestIfBadThingTouchesOtherBadThing(newx, newy);
8994   }
8995   else if (element == EL_PENGUIN)
8996     TestIfFriendTouchesBadThing(newx, newy);
8997
8998   if (DONT_GET_HIT_BY(element))
8999   {
9000     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9001   }
9002
9003   // give the player one last chance (one more frame) to move away
9004   if (CAN_FALL(element) && direction == MV_DOWN &&
9005       (last_line || (!IS_FREE(x, newy + 1) &&
9006                      (!IS_PLAYER(x, newy + 1) ||
9007                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9008     Impact(x, newy);
9009
9010   if (pushed_by_player && !game.use_change_when_pushing_bug)
9011   {
9012     int push_side = MV_DIR_OPPOSITE(direction);
9013     struct PlayerInfo *player = PLAYERINFO(x, y);
9014
9015     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9016                                player->index_bit, push_side);
9017     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
9018                                         player->index_bit, push_side);
9019   }
9020
9021   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
9022     MovDelay[newx][newy] = 1;
9023
9024   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9025
9026   TestIfElementTouchesCustomElement(x, y);      // empty or new element
9027   TestIfElementHitsCustomElement(newx, newy, direction);
9028   TestIfPlayerTouchesCustomElement(newx, newy);
9029   TestIfElementTouchesCustomElement(newx, newy);
9030
9031   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9032       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9033     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9034                              MV_DIR_OPPOSITE(direction));
9035 }
9036
9037 int AmoebaNeighbourNr(int ax, int ay)
9038 {
9039   int i;
9040   int element = Tile[ax][ay];
9041   int group_nr = 0;
9042   struct XY *xy = xy_topdown;
9043
9044   for (i = 0; i < NUM_DIRECTIONS; i++)
9045   {
9046     int x = ax + xy[i].x;
9047     int y = ay + xy[i].y;
9048
9049     if (!IN_LEV_FIELD(x, y))
9050       continue;
9051
9052     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
9053       group_nr = AmoebaNr[x][y];
9054   }
9055
9056   return group_nr;
9057 }
9058
9059 static void AmoebaMerge(int ax, int ay)
9060 {
9061   int i, x, y, xx, yy;
9062   int new_group_nr = AmoebaNr[ax][ay];
9063   struct XY *xy = xy_topdown;
9064
9065   if (new_group_nr == 0)
9066     return;
9067
9068   for (i = 0; i < NUM_DIRECTIONS; i++)
9069   {
9070     x = ax + xy[i].x;
9071     y = ay + xy[i].y;
9072
9073     if (!IN_LEV_FIELD(x, y))
9074       continue;
9075
9076     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9077          Tile[x][y] == EL_BD_AMOEBA ||
9078          Tile[x][y] == EL_AMOEBA_DEAD) &&
9079         AmoebaNr[x][y] != new_group_nr)
9080     {
9081       int old_group_nr = AmoebaNr[x][y];
9082
9083       if (old_group_nr == 0)
9084         return;
9085
9086       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9087       AmoebaCnt[old_group_nr] = 0;
9088       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9089       AmoebaCnt2[old_group_nr] = 0;
9090
9091       SCAN_PLAYFIELD(xx, yy)
9092       {
9093         if (AmoebaNr[xx][yy] == old_group_nr)
9094           AmoebaNr[xx][yy] = new_group_nr;
9095       }
9096     }
9097   }
9098 }
9099
9100 void AmoebaToDiamond(int ax, int ay)
9101 {
9102   int i, x, y;
9103
9104   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9105   {
9106     int group_nr = AmoebaNr[ax][ay];
9107
9108 #ifdef DEBUG
9109     if (group_nr == 0)
9110     {
9111       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9112       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9113
9114       return;
9115     }
9116 #endif
9117
9118     SCAN_PLAYFIELD(x, y)
9119     {
9120       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9121       {
9122         AmoebaNr[x][y] = 0;
9123         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9124       }
9125     }
9126
9127     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9128                             SND_AMOEBA_TURNING_TO_GEM :
9129                             SND_AMOEBA_TURNING_TO_ROCK));
9130     Bang(ax, ay);
9131   }
9132   else
9133   {
9134     struct XY *xy = xy_topdown;
9135
9136     for (i = 0; i < NUM_DIRECTIONS; i++)
9137     {
9138       x = ax + xy[i].x;
9139       y = ay + xy[i].y;
9140
9141       if (!IN_LEV_FIELD(x, y))
9142         continue;
9143
9144       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9145       {
9146         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9147                               SND_AMOEBA_TURNING_TO_GEM :
9148                               SND_AMOEBA_TURNING_TO_ROCK));
9149         Bang(x, y);
9150       }
9151     }
9152   }
9153 }
9154
9155 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9156 {
9157   int x, y;
9158   int group_nr = AmoebaNr[ax][ay];
9159   boolean done = FALSE;
9160
9161 #ifdef DEBUG
9162   if (group_nr == 0)
9163   {
9164     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9165     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9166
9167     return;
9168   }
9169 #endif
9170
9171   SCAN_PLAYFIELD(x, y)
9172   {
9173     if (AmoebaNr[x][y] == group_nr &&
9174         (Tile[x][y] == EL_AMOEBA_DEAD ||
9175          Tile[x][y] == EL_BD_AMOEBA ||
9176          Tile[x][y] == EL_AMOEBA_GROWING))
9177     {
9178       AmoebaNr[x][y] = 0;
9179       Tile[x][y] = new_element;
9180       InitField(x, y, FALSE);
9181       TEST_DrawLevelField(x, y);
9182       done = TRUE;
9183     }
9184   }
9185
9186   if (done)
9187     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9188                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9189                             SND_BD_AMOEBA_TURNING_TO_GEM));
9190 }
9191
9192 static void AmoebaGrowing(int x, int y)
9193 {
9194   static DelayCounter sound_delay = { 0 };
9195
9196   if (!MovDelay[x][y])          // start new growing cycle
9197   {
9198     MovDelay[x][y] = 7;
9199
9200     if (DelayReached(&sound_delay))
9201     {
9202       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9203       sound_delay.value = 30;
9204     }
9205   }
9206
9207   if (MovDelay[x][y])           // wait some time before growing bigger
9208   {
9209     MovDelay[x][y]--;
9210     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9211     {
9212       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9213                                            6 - MovDelay[x][y]);
9214
9215       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9216     }
9217
9218     if (!MovDelay[x][y])
9219     {
9220       Tile[x][y] = Store[x][y];
9221       Store[x][y] = 0;
9222       TEST_DrawLevelField(x, y);
9223     }
9224   }
9225 }
9226
9227 static void AmoebaShrinking(int x, int y)
9228 {
9229   static DelayCounter sound_delay = { 0 };
9230
9231   if (!MovDelay[x][y])          // start new shrinking cycle
9232   {
9233     MovDelay[x][y] = 7;
9234
9235     if (DelayReached(&sound_delay))
9236       sound_delay.value = 30;
9237   }
9238
9239   if (MovDelay[x][y])           // wait some time before shrinking
9240   {
9241     MovDelay[x][y]--;
9242     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9243     {
9244       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9245                                            6 - MovDelay[x][y]);
9246
9247       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9248     }
9249
9250     if (!MovDelay[x][y])
9251     {
9252       Tile[x][y] = EL_EMPTY;
9253       TEST_DrawLevelField(x, y);
9254
9255       // don't let mole enter this field in this cycle;
9256       // (give priority to objects falling to this field from above)
9257       Stop[x][y] = TRUE;
9258     }
9259   }
9260 }
9261
9262 static void AmoebaReproduce(int ax, int ay)
9263 {
9264   int i;
9265   int element = Tile[ax][ay];
9266   int graphic = el2img(element);
9267   int newax = ax, neway = ay;
9268   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9269   struct XY *xy = xy_topdown;
9270
9271   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9272   {
9273     Tile[ax][ay] = EL_AMOEBA_DEAD;
9274     TEST_DrawLevelField(ax, ay);
9275     return;
9276   }
9277
9278   if (IS_ANIMATED(graphic))
9279     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9280
9281   if (!MovDelay[ax][ay])        // start making new amoeba field
9282     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9283
9284   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9285   {
9286     MovDelay[ax][ay]--;
9287     if (MovDelay[ax][ay])
9288       return;
9289   }
9290
9291   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9292   {
9293     int start = RND(4);
9294     int x = ax + xy[start].x;
9295     int y = ay + xy[start].y;
9296
9297     if (!IN_LEV_FIELD(x, y))
9298       return;
9299
9300     if (IS_FREE(x, y) ||
9301         CAN_GROW_INTO(Tile[x][y]) ||
9302         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9303         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9304     {
9305       newax = x;
9306       neway = y;
9307     }
9308
9309     if (newax == ax && neway == ay)
9310       return;
9311   }
9312   else                          // normal or "filled" (BD style) amoeba
9313   {
9314     int start = RND(4);
9315     boolean waiting_for_player = FALSE;
9316
9317     for (i = 0; i < NUM_DIRECTIONS; i++)
9318     {
9319       int j = (start + i) % 4;
9320       int x = ax + xy[j].x;
9321       int y = ay + xy[j].y;
9322
9323       if (!IN_LEV_FIELD(x, y))
9324         continue;
9325
9326       if (IS_FREE(x, y) ||
9327           CAN_GROW_INTO(Tile[x][y]) ||
9328           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9329           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9330       {
9331         newax = x;
9332         neway = y;
9333         break;
9334       }
9335       else if (IS_PLAYER(x, y))
9336         waiting_for_player = TRUE;
9337     }
9338
9339     if (newax == ax && neway == ay)             // amoeba cannot grow
9340     {
9341       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9342       {
9343         Tile[ax][ay] = EL_AMOEBA_DEAD;
9344         TEST_DrawLevelField(ax, ay);
9345         AmoebaCnt[AmoebaNr[ax][ay]]--;
9346
9347         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9348         {
9349           if (element == EL_AMOEBA_FULL)
9350             AmoebaToDiamond(ax, ay);
9351           else if (element == EL_BD_AMOEBA)
9352             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9353         }
9354       }
9355       return;
9356     }
9357     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9358     {
9359       // amoeba gets larger by growing in some direction
9360
9361       int new_group_nr = AmoebaNr[ax][ay];
9362
9363 #ifdef DEBUG
9364   if (new_group_nr == 0)
9365   {
9366     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9367           newax, neway);
9368     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9369
9370     return;
9371   }
9372 #endif
9373
9374       AmoebaNr[newax][neway] = new_group_nr;
9375       AmoebaCnt[new_group_nr]++;
9376       AmoebaCnt2[new_group_nr]++;
9377
9378       // if amoeba touches other amoeba(s) after growing, unify them
9379       AmoebaMerge(newax, neway);
9380
9381       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9382       {
9383         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9384         return;
9385       }
9386     }
9387   }
9388
9389   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9390       (neway == lev_fieldy - 1 && newax != ax))
9391   {
9392     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9393     Store[newax][neway] = element;
9394   }
9395   else if (neway == ay || element == EL_EMC_DRIPPER)
9396   {
9397     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9398
9399     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9400   }
9401   else
9402   {
9403     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9404     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9405     Store[ax][ay] = EL_AMOEBA_DROP;
9406     ContinueMoving(ax, ay);
9407     return;
9408   }
9409
9410   TEST_DrawLevelField(newax, neway);
9411 }
9412
9413 static void Life(int ax, int ay)
9414 {
9415   int x1, y1, x2, y2;
9416   int life_time = 40;
9417   int element = Tile[ax][ay];
9418   int graphic = el2img(element);
9419   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9420                          level.biomaze);
9421   boolean changed = FALSE;
9422
9423   if (IS_ANIMATED(graphic))
9424     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9425
9426   if (Stop[ax][ay])
9427     return;
9428
9429   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9430     MovDelay[ax][ay] = life_time;
9431
9432   if (MovDelay[ax][ay])         // wait some time before next cycle
9433   {
9434     MovDelay[ax][ay]--;
9435     if (MovDelay[ax][ay])
9436       return;
9437   }
9438
9439   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9440   {
9441     int xx = ax+x1, yy = ay+y1;
9442     int old_element = Tile[xx][yy];
9443     int num_neighbours = 0;
9444
9445     if (!IN_LEV_FIELD(xx, yy))
9446       continue;
9447
9448     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9449     {
9450       int x = xx+x2, y = yy+y2;
9451
9452       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9453         continue;
9454
9455       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9456       boolean is_neighbour = FALSE;
9457
9458       if (level.use_life_bugs)
9459         is_neighbour =
9460           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9461            (IS_FREE(x, y)                             &&  Stop[x][y]));
9462       else
9463         is_neighbour =
9464           (Last[x][y] == element || is_player_cell);
9465
9466       if (is_neighbour)
9467         num_neighbours++;
9468     }
9469
9470     boolean is_free = FALSE;
9471
9472     if (level.use_life_bugs)
9473       is_free = (IS_FREE(xx, yy));
9474     else
9475       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9476
9477     if (xx == ax && yy == ay)           // field in the middle
9478     {
9479       if (num_neighbours < life_parameter[0] ||
9480           num_neighbours > life_parameter[1])
9481       {
9482         Tile[xx][yy] = EL_EMPTY;
9483         if (Tile[xx][yy] != old_element)
9484           TEST_DrawLevelField(xx, yy);
9485         Stop[xx][yy] = TRUE;
9486         changed = TRUE;
9487       }
9488     }
9489     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9490     {                                   // free border field
9491       if (num_neighbours >= life_parameter[2] &&
9492           num_neighbours <= life_parameter[3])
9493       {
9494         Tile[xx][yy] = element;
9495         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9496         if (Tile[xx][yy] != old_element)
9497           TEST_DrawLevelField(xx, yy);
9498         Stop[xx][yy] = TRUE;
9499         changed = TRUE;
9500       }
9501     }
9502   }
9503
9504   if (changed)
9505     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9506                    SND_GAME_OF_LIFE_GROWING);
9507 }
9508
9509 static void InitRobotWheel(int x, int y)
9510 {
9511   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9512 }
9513
9514 static void RunRobotWheel(int x, int y)
9515 {
9516   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9517 }
9518
9519 static void StopRobotWheel(int x, int y)
9520 {
9521   if (game.robot_wheel_x == x &&
9522       game.robot_wheel_y == y)
9523   {
9524     game.robot_wheel_x = -1;
9525     game.robot_wheel_y = -1;
9526     game.robot_wheel_active = FALSE;
9527   }
9528 }
9529
9530 static void InitTimegateWheel(int x, int y)
9531 {
9532   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9533 }
9534
9535 static void RunTimegateWheel(int x, int y)
9536 {
9537   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9538 }
9539
9540 static void InitMagicBallDelay(int x, int y)
9541 {
9542   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9543 }
9544
9545 static void ActivateMagicBall(int bx, int by)
9546 {
9547   int x, y;
9548
9549   if (level.ball_random)
9550   {
9551     int pos_border = RND(8);    // select one of the eight border elements
9552     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9553     int xx = pos_content % 3;
9554     int yy = pos_content / 3;
9555
9556     x = bx - 1 + xx;
9557     y = by - 1 + yy;
9558
9559     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9560       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9561   }
9562   else
9563   {
9564     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9565     {
9566       int xx = x - bx + 1;
9567       int yy = y - by + 1;
9568
9569       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9570         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9571     }
9572   }
9573
9574   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9575 }
9576
9577 static void CheckExit(int x, int y)
9578 {
9579   if (game.gems_still_needed > 0 ||
9580       game.sokoban_fields_still_needed > 0 ||
9581       game.sokoban_objects_still_needed > 0 ||
9582       game.lights_still_needed > 0)
9583   {
9584     int element = Tile[x][y];
9585     int graphic = el2img(element);
9586
9587     if (IS_ANIMATED(graphic))
9588       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9589
9590     return;
9591   }
9592
9593   // do not re-open exit door closed after last player
9594   if (game.all_players_gone)
9595     return;
9596
9597   Tile[x][y] = EL_EXIT_OPENING;
9598
9599   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9600 }
9601
9602 static void CheckExitEM(int x, int y)
9603 {
9604   if (game.gems_still_needed > 0 ||
9605       game.sokoban_fields_still_needed > 0 ||
9606       game.sokoban_objects_still_needed > 0 ||
9607       game.lights_still_needed > 0)
9608   {
9609     int element = Tile[x][y];
9610     int graphic = el2img(element);
9611
9612     if (IS_ANIMATED(graphic))
9613       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9614
9615     return;
9616   }
9617
9618   // do not re-open exit door closed after last player
9619   if (game.all_players_gone)
9620     return;
9621
9622   Tile[x][y] = EL_EM_EXIT_OPENING;
9623
9624   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9625 }
9626
9627 static void CheckExitSteel(int x, int y)
9628 {
9629   if (game.gems_still_needed > 0 ||
9630       game.sokoban_fields_still_needed > 0 ||
9631       game.sokoban_objects_still_needed > 0 ||
9632       game.lights_still_needed > 0)
9633   {
9634     int element = Tile[x][y];
9635     int graphic = el2img(element);
9636
9637     if (IS_ANIMATED(graphic))
9638       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9639
9640     return;
9641   }
9642
9643   // do not re-open exit door closed after last player
9644   if (game.all_players_gone)
9645     return;
9646
9647   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9648
9649   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9650 }
9651
9652 static void CheckExitSteelEM(int x, int y)
9653 {
9654   if (game.gems_still_needed > 0 ||
9655       game.sokoban_fields_still_needed > 0 ||
9656       game.sokoban_objects_still_needed > 0 ||
9657       game.lights_still_needed > 0)
9658   {
9659     int element = Tile[x][y];
9660     int graphic = el2img(element);
9661
9662     if (IS_ANIMATED(graphic))
9663       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9664
9665     return;
9666   }
9667
9668   // do not re-open exit door closed after last player
9669   if (game.all_players_gone)
9670     return;
9671
9672   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9673
9674   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9675 }
9676
9677 static void CheckExitSP(int x, int y)
9678 {
9679   if (game.gems_still_needed > 0)
9680   {
9681     int element = Tile[x][y];
9682     int graphic = el2img(element);
9683
9684     if (IS_ANIMATED(graphic))
9685       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9686
9687     return;
9688   }
9689
9690   // do not re-open exit door closed after last player
9691   if (game.all_players_gone)
9692     return;
9693
9694   Tile[x][y] = EL_SP_EXIT_OPENING;
9695
9696   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9697 }
9698
9699 static void CloseAllOpenTimegates(void)
9700 {
9701   int x, y;
9702
9703   SCAN_PLAYFIELD(x, y)
9704   {
9705     int element = Tile[x][y];
9706
9707     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9708     {
9709       Tile[x][y] = EL_TIMEGATE_CLOSING;
9710
9711       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9712     }
9713   }
9714 }
9715
9716 static void DrawTwinkleOnField(int x, int y)
9717 {
9718   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9719     return;
9720
9721   if (Tile[x][y] == EL_BD_DIAMOND)
9722     return;
9723
9724   if (MovDelay[x][y] == 0)      // next animation frame
9725     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9726
9727   if (MovDelay[x][y] != 0)      // wait some time before next frame
9728   {
9729     MovDelay[x][y]--;
9730
9731     DrawLevelElementAnimation(x, y, Tile[x][y]);
9732
9733     if (MovDelay[x][y] != 0)
9734     {
9735       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9736                                            10 - MovDelay[x][y]);
9737
9738       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9739     }
9740   }
9741 }
9742
9743 static void WallGrowing(int x, int y)
9744 {
9745   int delay = 6;
9746
9747   if (!MovDelay[x][y])          // next animation frame
9748     MovDelay[x][y] = 3 * delay;
9749
9750   if (MovDelay[x][y])           // wait some time before next frame
9751   {
9752     MovDelay[x][y]--;
9753
9754     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9755     {
9756       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9757       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9758
9759       DrawLevelGraphic(x, y, graphic, frame);
9760     }
9761
9762     if (!MovDelay[x][y])
9763     {
9764       if (MovDir[x][y] == MV_LEFT)
9765       {
9766         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9767           TEST_DrawLevelField(x - 1, y);
9768       }
9769       else if (MovDir[x][y] == MV_RIGHT)
9770       {
9771         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9772           TEST_DrawLevelField(x + 1, y);
9773       }
9774       else if (MovDir[x][y] == MV_UP)
9775       {
9776         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9777           TEST_DrawLevelField(x, y - 1);
9778       }
9779       else
9780       {
9781         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9782           TEST_DrawLevelField(x, y + 1);
9783       }
9784
9785       Tile[x][y] = Store[x][y];
9786       Store[x][y] = 0;
9787       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9788       TEST_DrawLevelField(x, y);
9789     }
9790   }
9791 }
9792
9793 static void CheckWallGrowing(int ax, int ay)
9794 {
9795   int element = Tile[ax][ay];
9796   int graphic = el2img(element);
9797   boolean free_top    = FALSE;
9798   boolean free_bottom = FALSE;
9799   boolean free_left   = FALSE;
9800   boolean free_right  = FALSE;
9801   boolean stop_top    = FALSE;
9802   boolean stop_bottom = FALSE;
9803   boolean stop_left   = FALSE;
9804   boolean stop_right  = FALSE;
9805   boolean new_wall    = FALSE;
9806
9807   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9808                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9809                            element == EL_EXPANDABLE_STEELWALL_ANY);
9810
9811   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9812                              element == EL_EXPANDABLE_WALL_ANY ||
9813                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9814                              element == EL_EXPANDABLE_STEELWALL_ANY);
9815
9816   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9817                              element == EL_EXPANDABLE_WALL_ANY ||
9818                              element == EL_EXPANDABLE_WALL ||
9819                              element == EL_BD_EXPANDABLE_WALL ||
9820                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9821                              element == EL_EXPANDABLE_STEELWALL_ANY);
9822
9823   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9824                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9825
9826   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9827                              element == EL_EXPANDABLE_WALL ||
9828                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9829
9830   int wall_growing = (is_steelwall ?
9831                       EL_EXPANDABLE_STEELWALL_GROWING :
9832                       EL_EXPANDABLE_WALL_GROWING);
9833
9834   int gfx_wall_growing_up    = (is_steelwall ?
9835                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9836                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9837   int gfx_wall_growing_down  = (is_steelwall ?
9838                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9839                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9840   int gfx_wall_growing_left  = (is_steelwall ?
9841                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9842                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9843   int gfx_wall_growing_right = (is_steelwall ?
9844                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9845                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9846
9847   if (IS_ANIMATED(graphic))
9848     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9849
9850   if (!MovDelay[ax][ay])        // start building new wall
9851     MovDelay[ax][ay] = 6;
9852
9853   if (MovDelay[ax][ay])         // wait some time before building new wall
9854   {
9855     MovDelay[ax][ay]--;
9856     if (MovDelay[ax][ay])
9857       return;
9858   }
9859
9860   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9861     free_top = TRUE;
9862   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9863     free_bottom = TRUE;
9864   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9865     free_left = TRUE;
9866   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9867     free_right = TRUE;
9868
9869   if (grow_vertical)
9870   {
9871     if (free_top)
9872     {
9873       Tile[ax][ay - 1] = wall_growing;
9874       Store[ax][ay - 1] = element;
9875       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9876
9877       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9878         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9879
9880       new_wall = TRUE;
9881     }
9882
9883     if (free_bottom)
9884     {
9885       Tile[ax][ay + 1] = wall_growing;
9886       Store[ax][ay + 1] = element;
9887       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9888
9889       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9890         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9891
9892       new_wall = TRUE;
9893     }
9894   }
9895
9896   if (grow_horizontal)
9897   {
9898     if (free_left)
9899     {
9900       Tile[ax - 1][ay] = wall_growing;
9901       Store[ax - 1][ay] = element;
9902       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9903
9904       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9905         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9906
9907       new_wall = TRUE;
9908     }
9909
9910     if (free_right)
9911     {
9912       Tile[ax + 1][ay] = wall_growing;
9913       Store[ax + 1][ay] = element;
9914       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9915
9916       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9917         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9918
9919       new_wall = TRUE;
9920     }
9921   }
9922
9923   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9924     TEST_DrawLevelField(ax, ay);
9925
9926   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9927     stop_top = TRUE;
9928   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9929     stop_bottom = TRUE;
9930   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9931     stop_left = TRUE;
9932   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9933     stop_right = TRUE;
9934
9935   if (((stop_top && stop_bottom) || stop_horizontal) &&
9936       ((stop_left && stop_right) || stop_vertical))
9937     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
9938
9939   if (new_wall)
9940     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9941 }
9942
9943 static void CheckForDragon(int x, int y)
9944 {
9945   int i, j;
9946   boolean dragon_found = FALSE;
9947   struct XY *xy = xy_topdown;
9948
9949   for (i = 0; i < NUM_DIRECTIONS; i++)
9950   {
9951     for (j = 0; j < 4; j++)
9952     {
9953       int xx = x + j * xy[i].x;
9954       int yy = y + j * xy[i].y;
9955
9956       if (IN_LEV_FIELD(xx, yy) &&
9957           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9958       {
9959         if (Tile[xx][yy] == EL_DRAGON)
9960           dragon_found = TRUE;
9961       }
9962       else
9963         break;
9964     }
9965   }
9966
9967   if (!dragon_found)
9968   {
9969     for (i = 0; i < NUM_DIRECTIONS; i++)
9970     {
9971       for (j = 0; j < 3; j++)
9972       {
9973         int xx = x + j * xy[i].x;
9974         int yy = y + j * xy[i].y;
9975
9976         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9977         {
9978           Tile[xx][yy] = EL_EMPTY;
9979           TEST_DrawLevelField(xx, yy);
9980         }
9981         else
9982           break;
9983       }
9984     }
9985   }
9986 }
9987
9988 static void InitBuggyBase(int x, int y)
9989 {
9990   int element = Tile[x][y];
9991   int activating_delay = FRAMES_PER_SECOND / 4;
9992
9993   ChangeDelay[x][y] =
9994     (element == EL_SP_BUGGY_BASE ?
9995      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9996      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9997      activating_delay :
9998      element == EL_SP_BUGGY_BASE_ACTIVE ?
9999      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10000 }
10001
10002 static void WarnBuggyBase(int x, int y)
10003 {
10004   int i;
10005   struct XY *xy = xy_topdown;
10006
10007   for (i = 0; i < NUM_DIRECTIONS; i++)
10008   {
10009     int xx = x + xy[i].x;
10010     int yy = y + xy[i].y;
10011
10012     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10013     {
10014       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10015
10016       break;
10017     }
10018   }
10019 }
10020
10021 static void InitTrap(int x, int y)
10022 {
10023   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10024 }
10025
10026 static void ActivateTrap(int x, int y)
10027 {
10028   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10029 }
10030
10031 static void ChangeActiveTrap(int x, int y)
10032 {
10033   int graphic = IMG_TRAP_ACTIVE;
10034
10035   // if new animation frame was drawn, correct crumbled sand border
10036   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10037     TEST_DrawLevelFieldCrumbled(x, y);
10038 }
10039
10040 static int getSpecialActionElement(int element, int number, int base_element)
10041 {
10042   return (element != EL_EMPTY ? element :
10043           number != -1 ? base_element + number - 1 :
10044           EL_EMPTY);
10045 }
10046
10047 static int getModifiedActionNumber(int value_old, int operator, int operand,
10048                                    int value_min, int value_max)
10049 {
10050   int value_new = (operator == CA_MODE_SET      ? operand :
10051                    operator == CA_MODE_ADD      ? value_old + operand :
10052                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10053                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10054                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10055                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10056                    value_old);
10057
10058   return (value_new < value_min ? value_min :
10059           value_new > value_max ? value_max :
10060           value_new);
10061 }
10062
10063 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10064 {
10065   struct ElementInfo *ei = &element_info[element];
10066   struct ElementChangeInfo *change = &ei->change_page[page];
10067   int target_element = change->target_element;
10068   int action_type = change->action_type;
10069   int action_mode = change->action_mode;
10070   int action_arg = change->action_arg;
10071   int action_element = change->action_element;
10072   int i;
10073
10074   if (!change->has_action)
10075     return;
10076
10077   // ---------- determine action paramater values -----------------------------
10078
10079   int level_time_value =
10080     (level.time > 0 ? TimeLeft :
10081      TimePlayed);
10082
10083   int action_arg_element_raw =
10084     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10085      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10086      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10087      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10088      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10089      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10090      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10091      EL_EMPTY);
10092   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10093
10094   int action_arg_direction =
10095     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10096      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10097      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10098      change->actual_trigger_side :
10099      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10100      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10101      MV_NONE);
10102
10103   int action_arg_number_min =
10104     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10105      CA_ARG_MIN);
10106
10107   int action_arg_number_max =
10108     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10109      action_type == CA_SET_LEVEL_GEMS ? 999 :
10110      action_type == CA_SET_LEVEL_TIME ? 9999 :
10111      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10112      action_type == CA_SET_CE_VALUE ? 9999 :
10113      action_type == CA_SET_CE_SCORE ? 9999 :
10114      CA_ARG_MAX);
10115
10116   int action_arg_number_reset =
10117     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10118      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10119      action_type == CA_SET_LEVEL_TIME ? level.time :
10120      action_type == CA_SET_LEVEL_SCORE ? 0 :
10121      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10122      action_type == CA_SET_CE_SCORE ? 0 :
10123      0);
10124
10125   int action_arg_number =
10126     (action_arg <= CA_ARG_MAX ? action_arg :
10127      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10128      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10129      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10130      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10131      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10132      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10133      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10134      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10135      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10136      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10137      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10138      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10139      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10140      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10141      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10142      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10143      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10144      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10145      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10146      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10147      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10148      -1);
10149
10150   int action_arg_number_old =
10151     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10152      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10153      action_type == CA_SET_LEVEL_SCORE ? game.score :
10154      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10155      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10156      0);
10157
10158   int action_arg_number_new =
10159     getModifiedActionNumber(action_arg_number_old,
10160                             action_mode, action_arg_number,
10161                             action_arg_number_min, action_arg_number_max);
10162
10163   int trigger_player_bits =
10164     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10165      change->actual_trigger_player_bits : change->trigger_player);
10166
10167   int action_arg_player_bits =
10168     (action_arg >= CA_ARG_PLAYER_1 &&
10169      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10170      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10171      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10172      PLAYER_BITS_ANY);
10173
10174   // ---------- execute action  -----------------------------------------------
10175
10176   switch (action_type)
10177   {
10178     case CA_NO_ACTION:
10179     {
10180       return;
10181     }
10182
10183     // ---------- level actions  ----------------------------------------------
10184
10185     case CA_RESTART_LEVEL:
10186     {
10187       game.restart_level = TRUE;
10188
10189       break;
10190     }
10191
10192     case CA_SHOW_ENVELOPE:
10193     {
10194       int element = getSpecialActionElement(action_arg_element,
10195                                             action_arg_number, EL_ENVELOPE_1);
10196
10197       if (IS_ENVELOPE(element))
10198         local_player->show_envelope = element;
10199
10200       break;
10201     }
10202
10203     case CA_SET_LEVEL_TIME:
10204     {
10205       if (level.time > 0)       // only modify limited time value
10206       {
10207         TimeLeft = action_arg_number_new;
10208
10209         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10210
10211         DisplayGameControlValues();
10212
10213         if (!TimeLeft && game.time_limit)
10214           for (i = 0; i < MAX_PLAYERS; i++)
10215             KillPlayer(&stored_player[i]);
10216       }
10217
10218       break;
10219     }
10220
10221     case CA_SET_LEVEL_SCORE:
10222     {
10223       game.score = action_arg_number_new;
10224
10225       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10226
10227       DisplayGameControlValues();
10228
10229       break;
10230     }
10231
10232     case CA_SET_LEVEL_GEMS:
10233     {
10234       game.gems_still_needed = action_arg_number_new;
10235
10236       game.snapshot.collected_item = TRUE;
10237
10238       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10239
10240       DisplayGameControlValues();
10241
10242       break;
10243     }
10244
10245     case CA_SET_LEVEL_WIND:
10246     {
10247       game.wind_direction = action_arg_direction;
10248
10249       break;
10250     }
10251
10252     case CA_SET_LEVEL_RANDOM_SEED:
10253     {
10254       // ensure that setting a new random seed while playing is predictable
10255       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10256
10257       break;
10258     }
10259
10260     // ---------- player actions  ---------------------------------------------
10261
10262     case CA_MOVE_PLAYER:
10263     case CA_MOVE_PLAYER_NEW:
10264     {
10265       // automatically move to the next field in specified direction
10266       for (i = 0; i < MAX_PLAYERS; i++)
10267         if (trigger_player_bits & (1 << i))
10268           if (action_type == CA_MOVE_PLAYER ||
10269               stored_player[i].MovPos == 0)
10270             stored_player[i].programmed_action = action_arg_direction;
10271
10272       break;
10273     }
10274
10275     case CA_EXIT_PLAYER:
10276     {
10277       for (i = 0; i < MAX_PLAYERS; i++)
10278         if (action_arg_player_bits & (1 << i))
10279           ExitPlayer(&stored_player[i]);
10280
10281       if (game.players_still_needed == 0)
10282         LevelSolved();
10283
10284       break;
10285     }
10286
10287     case CA_KILL_PLAYER:
10288     {
10289       for (i = 0; i < MAX_PLAYERS; i++)
10290         if (action_arg_player_bits & (1 << i))
10291           KillPlayer(&stored_player[i]);
10292
10293       break;
10294     }
10295
10296     case CA_SET_PLAYER_KEYS:
10297     {
10298       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10299       int element = getSpecialActionElement(action_arg_element,
10300                                             action_arg_number, EL_KEY_1);
10301
10302       if (IS_KEY(element))
10303       {
10304         for (i = 0; i < MAX_PLAYERS; i++)
10305         {
10306           if (trigger_player_bits & (1 << i))
10307           {
10308             stored_player[i].key[KEY_NR(element)] = key_state;
10309
10310             DrawGameDoorValues();
10311           }
10312         }
10313       }
10314
10315       break;
10316     }
10317
10318     case CA_SET_PLAYER_SPEED:
10319     {
10320       for (i = 0; i < MAX_PLAYERS; i++)
10321       {
10322         if (trigger_player_bits & (1 << i))
10323         {
10324           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10325
10326           if (action_arg == CA_ARG_SPEED_FASTER &&
10327               stored_player[i].cannot_move)
10328           {
10329             action_arg_number = STEPSIZE_VERY_SLOW;
10330           }
10331           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10332                    action_arg == CA_ARG_SPEED_FASTER)
10333           {
10334             action_arg_number = 2;
10335             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10336                            CA_MODE_MULTIPLY);
10337           }
10338           else if (action_arg == CA_ARG_NUMBER_RESET)
10339           {
10340             action_arg_number = level.initial_player_stepsize[i];
10341           }
10342
10343           move_stepsize =
10344             getModifiedActionNumber(move_stepsize,
10345                                     action_mode,
10346                                     action_arg_number,
10347                                     action_arg_number_min,
10348                                     action_arg_number_max);
10349
10350           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10351         }
10352       }
10353
10354       break;
10355     }
10356
10357     case CA_SET_PLAYER_SHIELD:
10358     {
10359       for (i = 0; i < MAX_PLAYERS; i++)
10360       {
10361         if (trigger_player_bits & (1 << i))
10362         {
10363           if (action_arg == CA_ARG_SHIELD_OFF)
10364           {
10365             stored_player[i].shield_normal_time_left = 0;
10366             stored_player[i].shield_deadly_time_left = 0;
10367           }
10368           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10369           {
10370             stored_player[i].shield_normal_time_left = 999999;
10371           }
10372           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10373           {
10374             stored_player[i].shield_normal_time_left = 999999;
10375             stored_player[i].shield_deadly_time_left = 999999;
10376           }
10377         }
10378       }
10379
10380       break;
10381     }
10382
10383     case CA_SET_PLAYER_GRAVITY:
10384     {
10385       for (i = 0; i < MAX_PLAYERS; i++)
10386       {
10387         if (trigger_player_bits & (1 << i))
10388         {
10389           stored_player[i].gravity =
10390             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10391              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10392              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10393              stored_player[i].gravity);
10394         }
10395       }
10396
10397       break;
10398     }
10399
10400     case CA_SET_PLAYER_ARTWORK:
10401     {
10402       for (i = 0; i < MAX_PLAYERS; i++)
10403       {
10404         if (trigger_player_bits & (1 << i))
10405         {
10406           int artwork_element = action_arg_element;
10407
10408           if (action_arg == CA_ARG_ELEMENT_RESET)
10409             artwork_element =
10410               (level.use_artwork_element[i] ? level.artwork_element[i] :
10411                stored_player[i].element_nr);
10412
10413           if (stored_player[i].artwork_element != artwork_element)
10414             stored_player[i].Frame = 0;
10415
10416           stored_player[i].artwork_element = artwork_element;
10417
10418           SetPlayerWaiting(&stored_player[i], FALSE);
10419
10420           // set number of special actions for bored and sleeping animation
10421           stored_player[i].num_special_action_bored =
10422             get_num_special_action(artwork_element,
10423                                    ACTION_BORING_1, ACTION_BORING_LAST);
10424           stored_player[i].num_special_action_sleeping =
10425             get_num_special_action(artwork_element,
10426                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10427         }
10428       }
10429
10430       break;
10431     }
10432
10433     case CA_SET_PLAYER_INVENTORY:
10434     {
10435       for (i = 0; i < MAX_PLAYERS; i++)
10436       {
10437         struct PlayerInfo *player = &stored_player[i];
10438         int j, k;
10439
10440         if (trigger_player_bits & (1 << i))
10441         {
10442           int inventory_element = action_arg_element;
10443
10444           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10445               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10446               action_arg == CA_ARG_ELEMENT_ACTION)
10447           {
10448             int element = inventory_element;
10449             int collect_count = element_info[element].collect_count_initial;
10450
10451             if (!IS_CUSTOM_ELEMENT(element))
10452               collect_count = 1;
10453
10454             if (collect_count == 0)
10455               player->inventory_infinite_element = element;
10456             else
10457               for (k = 0; k < collect_count; k++)
10458                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10459                   player->inventory_element[player->inventory_size++] =
10460                     element;
10461           }
10462           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10463                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10464                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10465           {
10466             if (player->inventory_infinite_element != EL_UNDEFINED &&
10467                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10468                                      action_arg_element_raw))
10469               player->inventory_infinite_element = EL_UNDEFINED;
10470
10471             for (k = 0, j = 0; j < player->inventory_size; j++)
10472             {
10473               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10474                                         action_arg_element_raw))
10475                 player->inventory_element[k++] = player->inventory_element[j];
10476             }
10477
10478             player->inventory_size = k;
10479           }
10480           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10481           {
10482             if (player->inventory_size > 0)
10483             {
10484               for (j = 0; j < player->inventory_size - 1; j++)
10485                 player->inventory_element[j] = player->inventory_element[j + 1];
10486
10487               player->inventory_size--;
10488             }
10489           }
10490           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10491           {
10492             if (player->inventory_size > 0)
10493               player->inventory_size--;
10494           }
10495           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10496           {
10497             player->inventory_infinite_element = EL_UNDEFINED;
10498             player->inventory_size = 0;
10499           }
10500           else if (action_arg == CA_ARG_INVENTORY_RESET)
10501           {
10502             player->inventory_infinite_element = EL_UNDEFINED;
10503             player->inventory_size = 0;
10504
10505             if (level.use_initial_inventory[i])
10506             {
10507               for (j = 0; j < level.initial_inventory_size[i]; j++)
10508               {
10509                 int element = level.initial_inventory_content[i][j];
10510                 int collect_count = element_info[element].collect_count_initial;
10511
10512                 if (!IS_CUSTOM_ELEMENT(element))
10513                   collect_count = 1;
10514
10515                 if (collect_count == 0)
10516                   player->inventory_infinite_element = element;
10517                 else
10518                   for (k = 0; k < collect_count; k++)
10519                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10520                       player->inventory_element[player->inventory_size++] =
10521                         element;
10522               }
10523             }
10524           }
10525         }
10526       }
10527
10528       break;
10529     }
10530
10531     // ---------- CE actions  -------------------------------------------------
10532
10533     case CA_SET_CE_VALUE:
10534     {
10535       int last_ce_value = CustomValue[x][y];
10536
10537       CustomValue[x][y] = action_arg_number_new;
10538
10539       if (CustomValue[x][y] != last_ce_value)
10540       {
10541         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10542         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10543
10544         if (CustomValue[x][y] == 0)
10545         {
10546           // reset change counter (else CE_VALUE_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_VALUE_GETS_ZERO);
10550           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10551         }
10552       }
10553
10554       break;
10555     }
10556
10557     case CA_SET_CE_SCORE:
10558     {
10559       int last_ce_score = ei->collect_score;
10560
10561       ei->collect_score = action_arg_number_new;
10562
10563       if (ei->collect_score != last_ce_score)
10564       {
10565         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10566         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10567
10568         if (ei->collect_score == 0)
10569         {
10570           int xx, yy;
10571
10572           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10573           ChangeCount[x][y] = 0;        // allow at least one more change
10574
10575           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10576           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10577
10578           /*
10579             This is a very special case that seems to be a mixture between
10580             CheckElementChange() and CheckTriggeredElementChange(): while
10581             the first one only affects single elements that are triggered
10582             directly, the second one affects multiple elements in the playfield
10583             that are triggered indirectly by another element. This is a third
10584             case: Changing the CE score always affects multiple identical CEs,
10585             so every affected CE must be checked, not only the single CE for
10586             which the CE score was changed in the first place (as every instance
10587             of that CE shares the same CE score, and therefore also can change)!
10588           */
10589           SCAN_PLAYFIELD(xx, yy)
10590           {
10591             if (Tile[xx][yy] == element)
10592               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10593                                  CE_SCORE_GETS_ZERO);
10594           }
10595         }
10596       }
10597
10598       break;
10599     }
10600
10601     case CA_SET_CE_ARTWORK:
10602     {
10603       int artwork_element = action_arg_element;
10604       boolean reset_frame = FALSE;
10605       int xx, yy;
10606
10607       if (action_arg == CA_ARG_ELEMENT_RESET)
10608         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10609                            element);
10610
10611       if (ei->gfx_element != artwork_element)
10612         reset_frame = TRUE;
10613
10614       ei->gfx_element = artwork_element;
10615
10616       SCAN_PLAYFIELD(xx, yy)
10617       {
10618         if (Tile[xx][yy] == element)
10619         {
10620           if (reset_frame)
10621           {
10622             ResetGfxAnimation(xx, yy);
10623             ResetRandomAnimationValue(xx, yy);
10624           }
10625
10626           TEST_DrawLevelField(xx, yy);
10627         }
10628       }
10629
10630       break;
10631     }
10632
10633     // ---------- engine actions  ---------------------------------------------
10634
10635     case CA_SET_ENGINE_SCAN_MODE:
10636     {
10637       InitPlayfieldScanMode(action_arg);
10638
10639       break;
10640     }
10641
10642     default:
10643       break;
10644   }
10645 }
10646
10647 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10648 {
10649   int old_element = Tile[x][y];
10650   int new_element = GetElementFromGroupElement(element);
10651   int previous_move_direction = MovDir[x][y];
10652   int last_ce_value = CustomValue[x][y];
10653   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10654   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10655   boolean add_player_onto_element = (new_element_is_player &&
10656                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10657                                      IS_WALKABLE(old_element));
10658
10659   if (!add_player_onto_element)
10660   {
10661     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10662       RemoveMovingField(x, y);
10663     else
10664       RemoveField(x, y);
10665
10666     Tile[x][y] = new_element;
10667
10668     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10669       MovDir[x][y] = previous_move_direction;
10670
10671     if (element_info[new_element].use_last_ce_value)
10672       CustomValue[x][y] = last_ce_value;
10673
10674     InitField_WithBug1(x, y, FALSE);
10675
10676     new_element = Tile[x][y];   // element may have changed
10677
10678     ResetGfxAnimation(x, y);
10679     ResetRandomAnimationValue(x, y);
10680
10681     TEST_DrawLevelField(x, y);
10682
10683     if (GFX_CRUMBLED(new_element))
10684       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10685
10686     if (old_element == EL_EXPLOSION)
10687     {
10688       Store[x][y] = Store2[x][y] = 0;
10689
10690       // check if new element replaces an exploding player, requiring cleanup
10691       if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
10692         StorePlayer[x][y] = 0;
10693     }
10694
10695     // check if element under the player changes from accessible to unaccessible
10696     // (needed for special case of dropping element which then changes)
10697     // (must be checked after creating new element for walkable group elements)
10698     if (IS_PLAYER(x, y) && !player_explosion_protected &&
10699         IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10700     {
10701       KillPlayer(PLAYERINFO(x, y));
10702
10703       return;
10704     }
10705   }
10706
10707   // "ChangeCount" not set yet to allow "entered by player" change one time
10708   if (new_element_is_player)
10709     RelocatePlayer(x, y, new_element);
10710
10711   if (is_change)
10712     ChangeCount[x][y]++;        // count number of changes in the same frame
10713
10714   TestIfBadThingTouchesPlayer(x, y);
10715   TestIfPlayerTouchesCustomElement(x, y);
10716   TestIfElementTouchesCustomElement(x, y);
10717 }
10718
10719 static void CreateField(int x, int y, int element)
10720 {
10721   CreateFieldExt(x, y, element, FALSE);
10722 }
10723
10724 static void CreateElementFromChange(int x, int y, int element)
10725 {
10726   element = GET_VALID_RUNTIME_ELEMENT(element);
10727
10728   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10729   {
10730     int old_element = Tile[x][y];
10731
10732     // prevent changed element from moving in same engine frame
10733     // unless both old and new element can either fall or move
10734     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10735         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10736       Stop[x][y] = TRUE;
10737   }
10738
10739   CreateFieldExt(x, y, element, TRUE);
10740 }
10741
10742 static boolean ChangeElement(int x, int y, int element, int page)
10743 {
10744   struct ElementInfo *ei = &element_info[element];
10745   struct ElementChangeInfo *change = &ei->change_page[page];
10746   int ce_value = CustomValue[x][y];
10747   int ce_score = ei->collect_score;
10748   int target_element;
10749   int old_element = Tile[x][y];
10750
10751   // always use default change event to prevent running into a loop
10752   if (ChangeEvent[x][y] == -1)
10753     ChangeEvent[x][y] = CE_DELAY;
10754
10755   if (ChangeEvent[x][y] == CE_DELAY)
10756   {
10757     // reset actual trigger element, trigger player and action element
10758     change->actual_trigger_element = EL_EMPTY;
10759     change->actual_trigger_player = EL_EMPTY;
10760     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10761     change->actual_trigger_side = CH_SIDE_NONE;
10762     change->actual_trigger_ce_value = 0;
10763     change->actual_trigger_ce_score = 0;
10764     change->actual_trigger_x = -1;
10765     change->actual_trigger_y = -1;
10766   }
10767
10768   // do not change elements more than a specified maximum number of changes
10769   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10770     return FALSE;
10771
10772   ChangeCount[x][y]++;          // count number of changes in the same frame
10773
10774   if (ei->has_anim_event)
10775     HandleGlobalAnimEventByElementChange(element, page, x, y,
10776                                          change->actual_trigger_x,
10777                                          change->actual_trigger_y);
10778
10779   if (change->explode)
10780   {
10781     Bang(x, y);
10782
10783     return TRUE;
10784   }
10785
10786   if (change->use_target_content)
10787   {
10788     boolean complete_replace = TRUE;
10789     boolean can_replace[3][3];
10790     int xx, yy;
10791
10792     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10793     {
10794       boolean is_empty;
10795       boolean is_walkable;
10796       boolean is_diggable;
10797       boolean is_collectible;
10798       boolean is_removable;
10799       boolean is_destructible;
10800       int ex = x + xx - 1;
10801       int ey = y + yy - 1;
10802       int content_element = change->target_content.e[xx][yy];
10803       int e;
10804
10805       can_replace[xx][yy] = TRUE;
10806
10807       if (ex == x && ey == y)   // do not check changing element itself
10808         continue;
10809
10810       if (content_element == EL_EMPTY_SPACE)
10811       {
10812         can_replace[xx][yy] = FALSE;    // do not replace border with space
10813
10814         continue;
10815       }
10816
10817       if (!IN_LEV_FIELD(ex, ey))
10818       {
10819         can_replace[xx][yy] = FALSE;
10820         complete_replace = FALSE;
10821
10822         continue;
10823       }
10824
10825       e = Tile[ex][ey];
10826
10827       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10828         e = MovingOrBlocked2Element(ex, ey);
10829
10830       is_empty = (IS_FREE(ex, ey) ||
10831                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10832
10833       is_walkable     = (is_empty || IS_WALKABLE(e));
10834       is_diggable     = (is_empty || IS_DIGGABLE(e));
10835       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10836       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10837       is_removable    = (is_diggable || is_collectible);
10838
10839       can_replace[xx][yy] =
10840         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10841           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10842           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10843           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10844           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10845           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10846          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10847
10848       if (!can_replace[xx][yy])
10849         complete_replace = FALSE;
10850     }
10851
10852     if (!change->only_if_complete || complete_replace)
10853     {
10854       boolean something_has_changed = FALSE;
10855
10856       if (change->only_if_complete && change->use_random_replace &&
10857           RND(100) < change->random_percentage)
10858         return FALSE;
10859
10860       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10861       {
10862         int ex = x + xx - 1;
10863         int ey = y + yy - 1;
10864         int content_element;
10865
10866         if (can_replace[xx][yy] && (!change->use_random_replace ||
10867                                     RND(100) < change->random_percentage))
10868         {
10869           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10870             RemoveMovingField(ex, ey);
10871
10872           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10873
10874           content_element = change->target_content.e[xx][yy];
10875           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10876                                               ce_value, ce_score);
10877
10878           CreateElementFromChange(ex, ey, target_element);
10879
10880           something_has_changed = TRUE;
10881
10882           // for symmetry reasons, freeze newly created border elements
10883           if (ex != x || ey != y)
10884             Stop[ex][ey] = TRUE;        // no more moving in this frame
10885         }
10886       }
10887
10888       if (something_has_changed)
10889       {
10890         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10891         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10892       }
10893     }
10894   }
10895   else
10896   {
10897     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10898                                         ce_value, ce_score);
10899
10900     if (element == EL_DIAGONAL_GROWING ||
10901         element == EL_DIAGONAL_SHRINKING)
10902     {
10903       target_element = Store[x][y];
10904
10905       Store[x][y] = EL_EMPTY;
10906     }
10907
10908     // special case: element changes to player (and may be kept if walkable)
10909     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10910       CreateElementFromChange(x, y, EL_EMPTY);
10911
10912     CreateElementFromChange(x, y, target_element);
10913
10914     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10915     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10916   }
10917
10918   // this uses direct change before indirect change
10919   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10920
10921   return TRUE;
10922 }
10923
10924 static void HandleElementChange(int x, int y, int page)
10925 {
10926   int element = MovingOrBlocked2Element(x, y);
10927   struct ElementInfo *ei = &element_info[element];
10928   struct ElementChangeInfo *change = &ei->change_page[page];
10929   boolean handle_action_before_change = FALSE;
10930
10931 #ifdef DEBUG
10932   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10933       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10934   {
10935     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10936           x, y, element, element_info[element].token_name);
10937     Debug("game:playing:HandleElementChange", "This should never happen!");
10938   }
10939 #endif
10940
10941   // this can happen with classic bombs on walkable, changing elements
10942   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10943   {
10944     return;
10945   }
10946
10947   if (ChangeDelay[x][y] == 0)           // initialize element change
10948   {
10949     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10950
10951     if (change->can_change)
10952     {
10953       // !!! not clear why graphic animation should be reset at all here !!!
10954       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10955       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10956
10957       /*
10958         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10959
10960         When using an animation frame delay of 1 (this only happens with
10961         "sp_zonk.moving.left/right" in the classic graphics), the default
10962         (non-moving) animation shows wrong animation frames (while the
10963         moving animation, like "sp_zonk.moving.left/right", is correct,
10964         so this graphical bug never shows up with the classic graphics).
10965         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10966         be drawn instead of the correct frames 0,1,2,3. This is caused by
10967         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10968         an element change: First when the change delay ("ChangeDelay[][]")
10969         counter has reached zero after decrementing, then a second time in
10970         the next frame (after "GfxFrame[][]" was already incremented) when
10971         "ChangeDelay[][]" is reset to the initial delay value again.
10972
10973         This causes frame 0 to be drawn twice, while the last frame won't
10974         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10975
10976         As some animations may already be cleverly designed around this bug
10977         (at least the "Snake Bite" snake tail animation does this), it cannot
10978         simply be fixed here without breaking such existing animations.
10979         Unfortunately, it cannot easily be detected if a graphics set was
10980         designed "before" or "after" the bug was fixed. As a workaround,
10981         a new graphics set option "game.graphics_engine_version" was added
10982         to be able to specify the game's major release version for which the
10983         graphics set was designed, which can then be used to decide if the
10984         bugfix should be used (version 4 and above) or not (version 3 or
10985         below, or if no version was specified at all, as with old sets).
10986
10987         (The wrong/fixed animation frames can be tested with the test level set
10988         "test_gfxframe" and level "000", which contains a specially prepared
10989         custom element at level position (x/y) == (11/9) which uses the zonk
10990         animation mentioned above. Using "game.graphics_engine_version: 4"
10991         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10992         This can also be seen from the debug output for this test element.)
10993       */
10994
10995       // when a custom element is about to change (for example by change delay),
10996       // do not reset graphic animation when the custom element is moving
10997       if (game.graphics_engine_version < 4 &&
10998           !IS_MOVING(x, y))
10999       {
11000         ResetGfxAnimation(x, y);
11001         ResetRandomAnimationValue(x, y);
11002       }
11003
11004       if (change->pre_change_function)
11005         change->pre_change_function(x, y);
11006     }
11007   }
11008
11009   ChangeDelay[x][y]--;
11010
11011   if (ChangeDelay[x][y] != 0)           // continue element change
11012   {
11013     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11014
11015     // also needed if CE can not change, but has CE delay with CE action
11016     if (IS_ANIMATED(graphic))
11017       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11018
11019     if (change->can_change)
11020     {
11021       if (change->change_function)
11022         change->change_function(x, y);
11023     }
11024   }
11025   else                                  // finish element change
11026   {
11027     if (ChangePage[x][y] != -1)         // remember page from delayed change
11028     {
11029       page = ChangePage[x][y];
11030       ChangePage[x][y] = -1;
11031
11032       change = &ei->change_page[page];
11033     }
11034
11035     if (IS_MOVING(x, y))                // never change a running system ;-)
11036     {
11037       ChangeDelay[x][y] = 1;            // try change after next move step
11038       ChangePage[x][y] = page;          // remember page to use for change
11039
11040       return;
11041     }
11042
11043     // special case: set new level random seed before changing element
11044     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11045       handle_action_before_change = TRUE;
11046
11047     if (change->has_action && handle_action_before_change)
11048       ExecuteCustomElementAction(x, y, element, page);
11049
11050     if (change->can_change)
11051     {
11052       if (ChangeElement(x, y, element, page))
11053       {
11054         if (change->post_change_function)
11055           change->post_change_function(x, y);
11056       }
11057     }
11058
11059     if (change->has_action && !handle_action_before_change)
11060       ExecuteCustomElementAction(x, y, element, page);
11061   }
11062 }
11063
11064 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11065                                               int trigger_element,
11066                                               int trigger_event,
11067                                               int trigger_player,
11068                                               int trigger_side,
11069                                               int trigger_page)
11070 {
11071   boolean change_done_any = FALSE;
11072   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11073   int i;
11074
11075   if (!(trigger_events[trigger_element][trigger_event]))
11076     return FALSE;
11077
11078   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11079
11080   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11081   {
11082     int element = EL_CUSTOM_START + i;
11083     boolean change_done = FALSE;
11084     int p;
11085
11086     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11087         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11088       continue;
11089
11090     for (p = 0; p < element_info[element].num_change_pages; p++)
11091     {
11092       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11093
11094       if (change->can_change_or_has_action &&
11095           change->has_event[trigger_event] &&
11096           change->trigger_side & trigger_side &&
11097           change->trigger_player & trigger_player &&
11098           change->trigger_page & trigger_page_bits &&
11099           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11100       {
11101         change->actual_trigger_element = trigger_element;
11102         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11103         change->actual_trigger_player_bits = trigger_player;
11104         change->actual_trigger_side = trigger_side;
11105         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11106         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11107         change->actual_trigger_x = trigger_x;
11108         change->actual_trigger_y = trigger_y;
11109
11110         if ((change->can_change && !change_done) || change->has_action)
11111         {
11112           int x, y;
11113
11114           SCAN_PLAYFIELD(x, y)
11115           {
11116             if (Tile[x][y] == element)
11117             {
11118               if (change->can_change && !change_done)
11119               {
11120                 // if element already changed in this frame, not only prevent
11121                 // another element change (checked in ChangeElement()), but
11122                 // also prevent additional element actions for this element
11123
11124                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11125                     !level.use_action_after_change_bug)
11126                   continue;
11127
11128                 ChangeDelay[x][y] = 1;
11129                 ChangeEvent[x][y] = trigger_event;
11130
11131                 HandleElementChange(x, y, p);
11132               }
11133               else if (change->has_action)
11134               {
11135                 // if element already changed in this frame, not only prevent
11136                 // another element change (checked in ChangeElement()), but
11137                 // also prevent additional element actions for this element
11138
11139                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11140                     !level.use_action_after_change_bug)
11141                   continue;
11142
11143                 ExecuteCustomElementAction(x, y, element, p);
11144                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11145               }
11146             }
11147           }
11148
11149           if (change->can_change)
11150           {
11151             change_done = TRUE;
11152             change_done_any = TRUE;
11153           }
11154         }
11155       }
11156     }
11157   }
11158
11159   RECURSION_LOOP_DETECTION_END();
11160
11161   return change_done_any;
11162 }
11163
11164 static boolean CheckElementChangeExt(int x, int y,
11165                                      int element,
11166                                      int trigger_element,
11167                                      int trigger_event,
11168                                      int trigger_player,
11169                                      int trigger_side)
11170 {
11171   boolean change_done = FALSE;
11172   int p;
11173
11174   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11175       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11176     return FALSE;
11177
11178   if (Tile[x][y] == EL_BLOCKED)
11179   {
11180     Blocked2Moving(x, y, &x, &y);
11181     element = Tile[x][y];
11182   }
11183
11184   // check if element has already changed or is about to change after moving
11185   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11186        Tile[x][y] != element) ||
11187
11188       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11189        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11190         ChangePage[x][y] != -1)))
11191     return FALSE;
11192
11193   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11194
11195   for (p = 0; p < element_info[element].num_change_pages; p++)
11196   {
11197     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11198
11199     /* check trigger element for all events where the element that is checked
11200        for changing interacts with a directly adjacent element -- this is
11201        different to element changes that affect other elements to change on the
11202        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11203     boolean check_trigger_element =
11204       (trigger_event == CE_NEXT_TO_X ||
11205        trigger_event == CE_TOUCHING_X ||
11206        trigger_event == CE_HITTING_X ||
11207        trigger_event == CE_HIT_BY_X ||
11208        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11209
11210     if (change->can_change_or_has_action &&
11211         change->has_event[trigger_event] &&
11212         change->trigger_side & trigger_side &&
11213         change->trigger_player & trigger_player &&
11214         (!check_trigger_element ||
11215          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11216     {
11217       change->actual_trigger_element = trigger_element;
11218       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11219       change->actual_trigger_player_bits = trigger_player;
11220       change->actual_trigger_side = trigger_side;
11221       change->actual_trigger_ce_value = CustomValue[x][y];
11222       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11223       change->actual_trigger_x = x;
11224       change->actual_trigger_y = y;
11225
11226       // special case: trigger element not at (x,y) position for some events
11227       if (check_trigger_element)
11228       {
11229         static struct
11230         {
11231           int dx, dy;
11232         } move_xy[] =
11233           {
11234             {  0,  0 },
11235             { -1,  0 },
11236             { +1,  0 },
11237             {  0,  0 },
11238             {  0, -1 },
11239             {  0,  0 }, { 0, 0 }, { 0, 0 },
11240             {  0, +1 }
11241           };
11242
11243         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11244         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11245
11246         change->actual_trigger_ce_value = CustomValue[xx][yy];
11247         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11248         change->actual_trigger_x = xx;
11249         change->actual_trigger_y = yy;
11250       }
11251
11252       if (change->can_change && !change_done)
11253       {
11254         ChangeDelay[x][y] = 1;
11255         ChangeEvent[x][y] = trigger_event;
11256
11257         HandleElementChange(x, y, p);
11258
11259         change_done = TRUE;
11260       }
11261       else if (change->has_action)
11262       {
11263         ExecuteCustomElementAction(x, y, element, p);
11264         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11265       }
11266     }
11267   }
11268
11269   RECURSION_LOOP_DETECTION_END();
11270
11271   return change_done;
11272 }
11273
11274 static void PlayPlayerSound(struct PlayerInfo *player)
11275 {
11276   int jx = player->jx, jy = player->jy;
11277   int sound_element = player->artwork_element;
11278   int last_action = player->last_action_waiting;
11279   int action = player->action_waiting;
11280
11281   if (player->is_waiting)
11282   {
11283     if (action != last_action)
11284       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11285     else
11286       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11287   }
11288   else
11289   {
11290     if (action != last_action)
11291       StopSound(element_info[sound_element].sound[last_action]);
11292
11293     if (last_action == ACTION_SLEEPING)
11294       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11295   }
11296 }
11297
11298 static void PlayAllPlayersSound(void)
11299 {
11300   int i;
11301
11302   for (i = 0; i < MAX_PLAYERS; i++)
11303     if (stored_player[i].active)
11304       PlayPlayerSound(&stored_player[i]);
11305 }
11306
11307 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11308 {
11309   boolean last_waiting = player->is_waiting;
11310   int move_dir = player->MovDir;
11311
11312   player->dir_waiting = move_dir;
11313   player->last_action_waiting = player->action_waiting;
11314
11315   if (is_waiting)
11316   {
11317     if (!last_waiting)          // not waiting -> waiting
11318     {
11319       player->is_waiting = TRUE;
11320
11321       player->frame_counter_bored =
11322         FrameCounter +
11323         game.player_boring_delay_fixed +
11324         GetSimpleRandom(game.player_boring_delay_random);
11325       player->frame_counter_sleeping =
11326         FrameCounter +
11327         game.player_sleeping_delay_fixed +
11328         GetSimpleRandom(game.player_sleeping_delay_random);
11329
11330       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11331     }
11332
11333     if (game.player_sleeping_delay_fixed +
11334         game.player_sleeping_delay_random > 0 &&
11335         player->anim_delay_counter == 0 &&
11336         player->post_delay_counter == 0 &&
11337         FrameCounter >= player->frame_counter_sleeping)
11338       player->is_sleeping = TRUE;
11339     else if (game.player_boring_delay_fixed +
11340              game.player_boring_delay_random > 0 &&
11341              FrameCounter >= player->frame_counter_bored)
11342       player->is_bored = TRUE;
11343
11344     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11345                               player->is_bored ? ACTION_BORING :
11346                               ACTION_WAITING);
11347
11348     if (player->is_sleeping && player->use_murphy)
11349     {
11350       // special case for sleeping Murphy when leaning against non-free tile
11351
11352       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11353           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11354            !IS_MOVING(player->jx - 1, player->jy)))
11355         move_dir = MV_LEFT;
11356       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11357                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11358                 !IS_MOVING(player->jx + 1, player->jy)))
11359         move_dir = MV_RIGHT;
11360       else
11361         player->is_sleeping = FALSE;
11362
11363       player->dir_waiting = move_dir;
11364     }
11365
11366     if (player->is_sleeping)
11367     {
11368       if (player->num_special_action_sleeping > 0)
11369       {
11370         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11371         {
11372           int last_special_action = player->special_action_sleeping;
11373           int num_special_action = player->num_special_action_sleeping;
11374           int special_action =
11375             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11376              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11377              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11378              last_special_action + 1 : ACTION_SLEEPING);
11379           int special_graphic =
11380             el_act_dir2img(player->artwork_element, special_action, move_dir);
11381
11382           player->anim_delay_counter =
11383             graphic_info[special_graphic].anim_delay_fixed +
11384             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11385           player->post_delay_counter =
11386             graphic_info[special_graphic].post_delay_fixed +
11387             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11388
11389           player->special_action_sleeping = special_action;
11390         }
11391
11392         if (player->anim_delay_counter > 0)
11393         {
11394           player->action_waiting = player->special_action_sleeping;
11395           player->anim_delay_counter--;
11396         }
11397         else if (player->post_delay_counter > 0)
11398         {
11399           player->post_delay_counter--;
11400         }
11401       }
11402     }
11403     else if (player->is_bored)
11404     {
11405       if (player->num_special_action_bored > 0)
11406       {
11407         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11408         {
11409           int special_action =
11410             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11411           int special_graphic =
11412             el_act_dir2img(player->artwork_element, special_action, move_dir);
11413
11414           player->anim_delay_counter =
11415             graphic_info[special_graphic].anim_delay_fixed +
11416             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11417           player->post_delay_counter =
11418             graphic_info[special_graphic].post_delay_fixed +
11419             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11420
11421           player->special_action_bored = special_action;
11422         }
11423
11424         if (player->anim_delay_counter > 0)
11425         {
11426           player->action_waiting = player->special_action_bored;
11427           player->anim_delay_counter--;
11428         }
11429         else if (player->post_delay_counter > 0)
11430         {
11431           player->post_delay_counter--;
11432         }
11433       }
11434     }
11435   }
11436   else if (last_waiting)        // waiting -> not waiting
11437   {
11438     player->is_waiting = FALSE;
11439     player->is_bored = FALSE;
11440     player->is_sleeping = FALSE;
11441
11442     player->frame_counter_bored = -1;
11443     player->frame_counter_sleeping = -1;
11444
11445     player->anim_delay_counter = 0;
11446     player->post_delay_counter = 0;
11447
11448     player->dir_waiting = player->MovDir;
11449     player->action_waiting = ACTION_DEFAULT;
11450
11451     player->special_action_bored = ACTION_DEFAULT;
11452     player->special_action_sleeping = ACTION_DEFAULT;
11453   }
11454 }
11455
11456 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11457 {
11458   if ((!player->is_moving  && player->was_moving) ||
11459       (player->MovPos == 0 && player->was_moving) ||
11460       (player->is_snapping && !player->was_snapping) ||
11461       (player->is_dropping && !player->was_dropping))
11462   {
11463     if (!CheckSaveEngineSnapshotToList())
11464       return;
11465
11466     player->was_moving = FALSE;
11467     player->was_snapping = TRUE;
11468     player->was_dropping = TRUE;
11469   }
11470   else
11471   {
11472     if (player->is_moving)
11473       player->was_moving = TRUE;
11474
11475     if (!player->is_snapping)
11476       player->was_snapping = FALSE;
11477
11478     if (!player->is_dropping)
11479       player->was_dropping = FALSE;
11480   }
11481
11482   static struct MouseActionInfo mouse_action_last = { 0 };
11483   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11484   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11485
11486   if (new_released)
11487     CheckSaveEngineSnapshotToList();
11488
11489   mouse_action_last = mouse_action;
11490 }
11491
11492 static void CheckSingleStepMode(struct PlayerInfo *player)
11493 {
11494   if (tape.single_step && tape.recording && !tape.pausing)
11495   {
11496     // as it is called "single step mode", just return to pause mode when the
11497     // player stopped moving after one tile (or never starts moving at all)
11498     // (reverse logic needed here in case single step mode used in team mode)
11499     if (player->is_moving ||
11500         player->is_pushing ||
11501         player->is_dropping_pressed ||
11502         player->effective_mouse_action.button)
11503       game.enter_single_step_mode = FALSE;
11504   }
11505
11506   CheckSaveEngineSnapshot(player);
11507 }
11508
11509 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11510 {
11511   int left      = player_action & JOY_LEFT;
11512   int right     = player_action & JOY_RIGHT;
11513   int up        = player_action & JOY_UP;
11514   int down      = player_action & JOY_DOWN;
11515   int button1   = player_action & JOY_BUTTON_1;
11516   int button2   = player_action & JOY_BUTTON_2;
11517   int dx        = (left ? -1 : right ? 1 : 0);
11518   int dy        = (up   ? -1 : down  ? 1 : 0);
11519
11520   if (!player->active || tape.pausing)
11521     return 0;
11522
11523   if (player_action)
11524   {
11525     if (button1)
11526       SnapField(player, dx, dy);
11527     else
11528     {
11529       if (button2)
11530         DropElement(player);
11531
11532       MovePlayer(player, dx, dy);
11533     }
11534
11535     CheckSingleStepMode(player);
11536
11537     SetPlayerWaiting(player, FALSE);
11538
11539     return player_action;
11540   }
11541   else
11542   {
11543     // no actions for this player (no input at player's configured device)
11544
11545     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11546     SnapField(player, 0, 0);
11547     CheckGravityMovementWhenNotMoving(player);
11548
11549     if (player->MovPos == 0)
11550       SetPlayerWaiting(player, TRUE);
11551
11552     if (player->MovPos == 0)    // needed for tape.playing
11553       player->is_moving = FALSE;
11554
11555     player->is_dropping = FALSE;
11556     player->is_dropping_pressed = FALSE;
11557     player->drop_pressed_delay = 0;
11558
11559     CheckSingleStepMode(player);
11560
11561     return 0;
11562   }
11563 }
11564
11565 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11566                                          byte *tape_action)
11567 {
11568   if (!tape.use_mouse_actions)
11569     return;
11570
11571   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11572   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11573   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11574 }
11575
11576 static void SetTapeActionFromMouseAction(byte *tape_action,
11577                                          struct MouseActionInfo *mouse_action)
11578 {
11579   if (!tape.use_mouse_actions)
11580     return;
11581
11582   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11583   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11584   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11585 }
11586
11587 static void CheckLevelSolved(void)
11588 {
11589   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11590   {
11591     if (game_em.level_solved &&
11592         !game_em.game_over)                             // game won
11593     {
11594       LevelSolved();
11595
11596       game_em.game_over = TRUE;
11597
11598       game.all_players_gone = TRUE;
11599     }
11600
11601     if (game_em.game_over)                              // game lost
11602       game.all_players_gone = TRUE;
11603   }
11604   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11605   {
11606     if (game_sp.level_solved &&
11607         !game_sp.game_over)                             // game won
11608     {
11609       LevelSolved();
11610
11611       game_sp.game_over = TRUE;
11612
11613       game.all_players_gone = TRUE;
11614     }
11615
11616     if (game_sp.game_over)                              // game lost
11617       game.all_players_gone = TRUE;
11618   }
11619   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11620   {
11621     if (game_mm.level_solved &&
11622         !game_mm.game_over)                             // game won
11623     {
11624       LevelSolved();
11625
11626       game_mm.game_over = TRUE;
11627
11628       game.all_players_gone = TRUE;
11629     }
11630
11631     if (game_mm.game_over)                              // game lost
11632       game.all_players_gone = TRUE;
11633   }
11634 }
11635
11636 static void CheckLevelTime_StepCounter(void)
11637 {
11638   int i;
11639
11640   TimePlayed++;
11641
11642   if (TimeLeft > 0)
11643   {
11644     TimeLeft--;
11645
11646     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11647       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11648
11649     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11650
11651     DisplayGameControlValues();
11652
11653     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11654       for (i = 0; i < MAX_PLAYERS; i++)
11655         KillPlayer(&stored_player[i]);
11656   }
11657   else if (game.no_level_time_limit && !game.all_players_gone)
11658   {
11659     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11660
11661     DisplayGameControlValues();
11662   }
11663 }
11664
11665 static void CheckLevelTime(void)
11666 {
11667   int i;
11668
11669   if (TimeFrames >= FRAMES_PER_SECOND)
11670   {
11671     TimeFrames = 0;
11672     TapeTime++;
11673
11674     for (i = 0; i < MAX_PLAYERS; i++)
11675     {
11676       struct PlayerInfo *player = &stored_player[i];
11677
11678       if (SHIELD_ON(player))
11679       {
11680         player->shield_normal_time_left--;
11681
11682         if (player->shield_deadly_time_left > 0)
11683           player->shield_deadly_time_left--;
11684       }
11685     }
11686
11687     if (!game.LevelSolved && !level.use_step_counter)
11688     {
11689       TimePlayed++;
11690
11691       if (TimeLeft > 0)
11692       {
11693         TimeLeft--;
11694
11695         if (TimeLeft <= 10 && game.time_limit)
11696           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11697
11698         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11699            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11700
11701         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11702
11703         if (!TimeLeft && game.time_limit)
11704         {
11705           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11706             game_em.lev->killed_out_of_time = TRUE;
11707           else
11708             for (i = 0; i < MAX_PLAYERS; i++)
11709               KillPlayer(&stored_player[i]);
11710         }
11711       }
11712       else if (game.no_level_time_limit && !game.all_players_gone)
11713       {
11714         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11715       }
11716
11717       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11718     }
11719
11720     if (tape.recording || tape.playing)
11721       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11722   }
11723
11724   if (tape.recording || tape.playing)
11725     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11726
11727   UpdateAndDisplayGameControlValues();
11728 }
11729
11730 void AdvanceFrameAndPlayerCounters(int player_nr)
11731 {
11732   int i;
11733
11734   // advance frame counters (global frame counter and time frame counter)
11735   FrameCounter++;
11736   TimeFrames++;
11737
11738   // advance player counters (counters for move delay, move animation etc.)
11739   for (i = 0; i < MAX_PLAYERS; i++)
11740   {
11741     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11742     int move_delay_value = stored_player[i].move_delay_value;
11743     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11744
11745     if (!advance_player_counters)       // not all players may be affected
11746       continue;
11747
11748     if (move_frames == 0)       // less than one move per game frame
11749     {
11750       int stepsize = TILEX / move_delay_value;
11751       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11752       int count = (stored_player[i].is_moving ?
11753                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11754
11755       if (count % delay == 0)
11756         move_frames = 1;
11757     }
11758
11759     stored_player[i].Frame += move_frames;
11760
11761     if (stored_player[i].MovPos != 0)
11762       stored_player[i].StepFrame += move_frames;
11763
11764     if (stored_player[i].move_delay > 0)
11765       stored_player[i].move_delay--;
11766
11767     // due to bugs in previous versions, counter must count up, not down
11768     if (stored_player[i].push_delay != -1)
11769       stored_player[i].push_delay++;
11770
11771     if (stored_player[i].drop_delay > 0)
11772       stored_player[i].drop_delay--;
11773
11774     if (stored_player[i].is_dropping_pressed)
11775       stored_player[i].drop_pressed_delay++;
11776   }
11777 }
11778
11779 void AdvanceFrameCounter(void)
11780 {
11781   FrameCounter++;
11782 }
11783
11784 void AdvanceGfxFrame(void)
11785 {
11786   int x, y;
11787
11788   SCAN_PLAYFIELD(x, y)
11789   {
11790     GfxFrame[x][y]++;
11791   }
11792 }
11793
11794 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11795                               struct MouseActionInfo *mouse_action_last)
11796 {
11797   if (mouse_action->button)
11798   {
11799     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11800     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11801     int x = mouse_action->lx;
11802     int y = mouse_action->ly;
11803     int element = Tile[x][y];
11804
11805     if (new_button)
11806     {
11807       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11808       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11809                                          ch_button);
11810     }
11811
11812     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11813     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
11814                                        ch_button);
11815
11816     if (level.use_step_counter)
11817     {
11818       boolean counted_click = FALSE;
11819
11820       // element clicked that can change when clicked/pressed
11821       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
11822           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
11823            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
11824         counted_click = TRUE;
11825
11826       // element clicked that can trigger change when clicked/pressed
11827       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
11828           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
11829         counted_click = TRUE;
11830
11831       if (new_button && counted_click)
11832         CheckLevelTime_StepCounter();
11833     }
11834   }
11835 }
11836
11837 void StartGameActions(boolean init_network_game, boolean record_tape,
11838                       int random_seed)
11839 {
11840   unsigned int new_random_seed = InitRND(random_seed);
11841
11842   if (record_tape)
11843     TapeStartRecording(new_random_seed);
11844
11845   if (setup.auto_pause_on_start && !tape.pausing)
11846     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11847
11848   if (init_network_game)
11849   {
11850     SendToServer_LevelFile();
11851     SendToServer_StartPlaying();
11852
11853     return;
11854   }
11855
11856   InitGame();
11857 }
11858
11859 static void GameActionsExt(void)
11860 {
11861 #if 0
11862   static unsigned int game_frame_delay = 0;
11863 #endif
11864   unsigned int game_frame_delay_value;
11865   byte *recorded_player_action;
11866   byte summarized_player_action = 0;
11867   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11868   int i;
11869
11870   // detect endless loops, caused by custom element programming
11871   if (recursion_loop_detected && recursion_loop_depth == 0)
11872   {
11873     char *message = getStringCat3("Internal Error! Element ",
11874                                   EL_NAME(recursion_loop_element),
11875                                   " caused endless loop! Quit the game?");
11876
11877     Warn("element '%s' caused endless loop in game engine",
11878          EL_NAME(recursion_loop_element));
11879
11880     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11881
11882     recursion_loop_detected = FALSE;    // if game should be continued
11883
11884     free(message);
11885
11886     return;
11887   }
11888
11889   if (game.restart_level)
11890     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11891
11892   CheckLevelSolved();
11893
11894   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11895     GameWon();
11896
11897   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11898     TapeStop();
11899
11900   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11901     return;
11902
11903   game_frame_delay_value =
11904     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11905
11906   if (tape.playing && tape.warp_forward && !tape.pausing)
11907     game_frame_delay_value = 0;
11908
11909   SetVideoFrameDelay(game_frame_delay_value);
11910
11911   // (de)activate virtual buttons depending on current game status
11912   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11913   {
11914     if (game.all_players_gone)  // if no players there to be controlled anymore
11915       SetOverlayActive(FALSE);
11916     else if (!tape.playing)     // if game continues after tape stopped playing
11917       SetOverlayActive(TRUE);
11918   }
11919
11920 #if 0
11921 #if 0
11922   // ---------- main game synchronization point ----------
11923
11924   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11925
11926   Debug("game:playing:skip", "skip == %d", skip);
11927
11928 #else
11929   // ---------- main game synchronization point ----------
11930
11931   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11932 #endif
11933 #endif
11934
11935   if (network_playing && !network_player_action_received)
11936   {
11937     // try to get network player actions in time
11938
11939     // last chance to get network player actions without main loop delay
11940     HandleNetworking();
11941
11942     // game was quit by network peer
11943     if (game_status != GAME_MODE_PLAYING)
11944       return;
11945
11946     // check if network player actions still missing and game still running
11947     if (!network_player_action_received && !checkGameEnded())
11948       return;           // failed to get network player actions in time
11949
11950     // do not yet reset "network_player_action_received" (for tape.pausing)
11951   }
11952
11953   if (tape.pausing)
11954     return;
11955
11956   // at this point we know that we really continue executing the game
11957
11958   network_player_action_received = FALSE;
11959
11960   // when playing tape, read previously recorded player input from tape data
11961   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11962
11963   local_player->effective_mouse_action = local_player->mouse_action;
11964
11965   if (recorded_player_action != NULL)
11966     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11967                                  recorded_player_action);
11968
11969   // TapePlayAction() may return NULL when toggling to "pause before death"
11970   if (tape.pausing)
11971     return;
11972
11973   if (tape.set_centered_player)
11974   {
11975     game.centered_player_nr_next = tape.centered_player_nr_next;
11976     game.set_centered_player = TRUE;
11977   }
11978
11979   for (i = 0; i < MAX_PLAYERS; i++)
11980   {
11981     summarized_player_action |= stored_player[i].action;
11982
11983     if (!network_playing && (game.team_mode || tape.playing))
11984       stored_player[i].effective_action = stored_player[i].action;
11985   }
11986
11987   if (network_playing && !checkGameEnded())
11988     SendToServer_MovePlayer(summarized_player_action);
11989
11990   // summarize all actions at local players mapped input device position
11991   // (this allows using different input devices in single player mode)
11992   if (!network.enabled && !game.team_mode)
11993     stored_player[map_player_action[local_player->index_nr]].effective_action =
11994       summarized_player_action;
11995
11996   // summarize all actions at centered player in local team mode
11997   if (tape.recording &&
11998       setup.team_mode && !network.enabled &&
11999       setup.input_on_focus &&
12000       game.centered_player_nr != -1)
12001   {
12002     for (i = 0; i < MAX_PLAYERS; i++)
12003       stored_player[map_player_action[i]].effective_action =
12004         (i == game.centered_player_nr ? summarized_player_action : 0);
12005   }
12006
12007   if (recorded_player_action != NULL)
12008     for (i = 0; i < MAX_PLAYERS; i++)
12009       stored_player[i].effective_action = recorded_player_action[i];
12010
12011   for (i = 0; i < MAX_PLAYERS; i++)
12012   {
12013     tape_action[i] = stored_player[i].effective_action;
12014
12015     /* (this may happen in the RND game engine if a player was not present on
12016        the playfield on level start, but appeared later from a custom element */
12017     if (setup.team_mode &&
12018         tape.recording &&
12019         tape_action[i] &&
12020         !tape.player_participates[i])
12021       tape.player_participates[i] = TRUE;
12022   }
12023
12024   SetTapeActionFromMouseAction(tape_action,
12025                                &local_player->effective_mouse_action);
12026
12027   // only record actions from input devices, but not programmed actions
12028   if (tape.recording)
12029     TapeRecordAction(tape_action);
12030
12031   // remember if game was played (especially after tape stopped playing)
12032   if (!tape.playing && summarized_player_action && !checkGameFailed())
12033     game.GamePlayed = TRUE;
12034
12035 #if USE_NEW_PLAYER_ASSIGNMENTS
12036   // !!! also map player actions in single player mode !!!
12037   // if (game.team_mode)
12038   if (1)
12039   {
12040     byte mapped_action[MAX_PLAYERS];
12041
12042 #if DEBUG_PLAYER_ACTIONS
12043     for (i = 0; i < MAX_PLAYERS; i++)
12044       DebugContinued("", "%d, ", stored_player[i].effective_action);
12045 #endif
12046
12047     for (i = 0; i < MAX_PLAYERS; i++)
12048       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12049
12050     for (i = 0; i < MAX_PLAYERS; i++)
12051       stored_player[i].effective_action = mapped_action[i];
12052
12053 #if DEBUG_PLAYER_ACTIONS
12054     DebugContinued("", "=> ");
12055     for (i = 0; i < MAX_PLAYERS; i++)
12056       DebugContinued("", "%d, ", stored_player[i].effective_action);
12057     DebugContinued("game:playing:player", "\n");
12058 #endif
12059   }
12060 #if DEBUG_PLAYER_ACTIONS
12061   else
12062   {
12063     for (i = 0; i < MAX_PLAYERS; i++)
12064       DebugContinued("", "%d, ", stored_player[i].effective_action);
12065     DebugContinued("game:playing:player", "\n");
12066   }
12067 #endif
12068 #endif
12069
12070   for (i = 0; i < MAX_PLAYERS; i++)
12071   {
12072     // allow engine snapshot in case of changed movement attempt
12073     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12074         (stored_player[i].effective_action & KEY_MOTION))
12075       game.snapshot.changed_action = TRUE;
12076
12077     // allow engine snapshot in case of snapping/dropping attempt
12078     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12079         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12080       game.snapshot.changed_action = TRUE;
12081
12082     game.snapshot.last_action[i] = stored_player[i].effective_action;
12083   }
12084
12085   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12086   {
12087     GameActions_EM_Main();
12088   }
12089   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12090   {
12091     GameActions_SP_Main();
12092   }
12093   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12094   {
12095     GameActions_MM_Main();
12096   }
12097   else
12098   {
12099     GameActions_RND_Main();
12100   }
12101
12102   BlitScreenToBitmap(backbuffer);
12103
12104   CheckLevelSolved();
12105   CheckLevelTime();
12106
12107   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12108
12109   if (global.show_frames_per_second)
12110   {
12111     static unsigned int fps_counter = 0;
12112     static int fps_frames = 0;
12113     unsigned int fps_delay_ms = Counter() - fps_counter;
12114
12115     fps_frames++;
12116
12117     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12118     {
12119       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12120
12121       fps_frames = 0;
12122       fps_counter = Counter();
12123
12124       // always draw FPS to screen after FPS value was updated
12125       redraw_mask |= REDRAW_FPS;
12126     }
12127
12128     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12129     if (GetDrawDeactivationMask() == REDRAW_NONE)
12130       redraw_mask |= REDRAW_FPS;
12131   }
12132 }
12133
12134 static void GameActions_CheckSaveEngineSnapshot(void)
12135 {
12136   if (!game.snapshot.save_snapshot)
12137     return;
12138
12139   // clear flag for saving snapshot _before_ saving snapshot
12140   game.snapshot.save_snapshot = FALSE;
12141
12142   SaveEngineSnapshotToList();
12143 }
12144
12145 void GameActions(void)
12146 {
12147   GameActionsExt();
12148
12149   GameActions_CheckSaveEngineSnapshot();
12150 }
12151
12152 void GameActions_EM_Main(void)
12153 {
12154   byte effective_action[MAX_PLAYERS];
12155   int i;
12156
12157   for (i = 0; i < MAX_PLAYERS; i++)
12158     effective_action[i] = stored_player[i].effective_action;
12159
12160   GameActions_EM(effective_action);
12161 }
12162
12163 void GameActions_SP_Main(void)
12164 {
12165   byte effective_action[MAX_PLAYERS];
12166   int i;
12167
12168   for (i = 0; i < MAX_PLAYERS; i++)
12169     effective_action[i] = stored_player[i].effective_action;
12170
12171   GameActions_SP(effective_action);
12172
12173   for (i = 0; i < MAX_PLAYERS; i++)
12174   {
12175     if (stored_player[i].force_dropping)
12176       stored_player[i].action |= KEY_BUTTON_DROP;
12177
12178     stored_player[i].force_dropping = FALSE;
12179   }
12180 }
12181
12182 void GameActions_MM_Main(void)
12183 {
12184   AdvanceGfxFrame();
12185
12186   GameActions_MM(local_player->effective_mouse_action);
12187 }
12188
12189 void GameActions_RND_Main(void)
12190 {
12191   GameActions_RND();
12192 }
12193
12194 void GameActions_RND(void)
12195 {
12196   static struct MouseActionInfo mouse_action_last = { 0 };
12197   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12198   int magic_wall_x = 0, magic_wall_y = 0;
12199   int i, x, y, element, graphic, last_gfx_frame;
12200
12201   InitPlayfieldScanModeVars();
12202
12203   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12204   {
12205     SCAN_PLAYFIELD(x, y)
12206     {
12207       ChangeCount[x][y] = 0;
12208       ChangeEvent[x][y] = -1;
12209     }
12210   }
12211
12212   if (game.set_centered_player)
12213   {
12214     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12215
12216     // switching to "all players" only possible if all players fit to screen
12217     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12218     {
12219       game.centered_player_nr_next = game.centered_player_nr;
12220       game.set_centered_player = FALSE;
12221     }
12222
12223     // do not switch focus to non-existing (or non-active) player
12224     if (game.centered_player_nr_next >= 0 &&
12225         !stored_player[game.centered_player_nr_next].active)
12226     {
12227       game.centered_player_nr_next = game.centered_player_nr;
12228       game.set_centered_player = FALSE;
12229     }
12230   }
12231
12232   if (game.set_centered_player &&
12233       ScreenMovPos == 0)        // screen currently aligned at tile position
12234   {
12235     int sx, sy;
12236
12237     if (game.centered_player_nr_next == -1)
12238     {
12239       setScreenCenteredToAllPlayers(&sx, &sy);
12240     }
12241     else
12242     {
12243       sx = stored_player[game.centered_player_nr_next].jx;
12244       sy = stored_player[game.centered_player_nr_next].jy;
12245     }
12246
12247     game.centered_player_nr = game.centered_player_nr_next;
12248     game.set_centered_player = FALSE;
12249
12250     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12251     DrawGameDoorValues();
12252   }
12253
12254   // check single step mode (set flag and clear again if any player is active)
12255   game.enter_single_step_mode =
12256     (tape.single_step && tape.recording && !tape.pausing);
12257
12258   for (i = 0; i < MAX_PLAYERS; i++)
12259   {
12260     int actual_player_action = stored_player[i].effective_action;
12261
12262 #if 1
12263     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12264        - rnd_equinox_tetrachloride 048
12265        - rnd_equinox_tetrachloride_ii 096
12266        - rnd_emanuel_schmieg 002
12267        - doctor_sloan_ww 001, 020
12268     */
12269     if (stored_player[i].MovPos == 0)
12270       CheckGravityMovement(&stored_player[i]);
12271 #endif
12272
12273     // overwrite programmed action with tape action
12274     if (stored_player[i].programmed_action)
12275       actual_player_action = stored_player[i].programmed_action;
12276
12277     PlayerActions(&stored_player[i], actual_player_action);
12278
12279     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12280   }
12281
12282   // single step pause mode may already have been toggled by "ScrollPlayer()"
12283   if (game.enter_single_step_mode && !tape.pausing)
12284     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12285
12286   ScrollScreen(NULL, SCROLL_GO_ON);
12287
12288   /* for backwards compatibility, the following code emulates a fixed bug that
12289      occured when pushing elements (causing elements that just made their last
12290      pushing step to already (if possible) make their first falling step in the
12291      same game frame, which is bad); this code is also needed to use the famous
12292      "spring push bug" which is used in older levels and might be wanted to be
12293      used also in newer levels, but in this case the buggy pushing code is only
12294      affecting the "spring" element and no other elements */
12295
12296   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12297   {
12298     for (i = 0; i < MAX_PLAYERS; i++)
12299     {
12300       struct PlayerInfo *player = &stored_player[i];
12301       int x = player->jx;
12302       int y = player->jy;
12303
12304       if (player->active && player->is_pushing && player->is_moving &&
12305           IS_MOVING(x, y) &&
12306           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12307            Tile[x][y] == EL_SPRING))
12308       {
12309         ContinueMoving(x, y);
12310
12311         // continue moving after pushing (this is actually a bug)
12312         if (!IS_MOVING(x, y))
12313           Stop[x][y] = FALSE;
12314       }
12315     }
12316   }
12317
12318   SCAN_PLAYFIELD(x, y)
12319   {
12320     Last[x][y] = Tile[x][y];
12321
12322     ChangeCount[x][y] = 0;
12323     ChangeEvent[x][y] = -1;
12324
12325     // this must be handled before main playfield loop
12326     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12327     {
12328       MovDelay[x][y]--;
12329       if (MovDelay[x][y] <= 0)
12330         RemoveField(x, y);
12331     }
12332
12333     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12334     {
12335       MovDelay[x][y]--;
12336       if (MovDelay[x][y] <= 0)
12337       {
12338         int element = Store[x][y];
12339         int move_direction = MovDir[x][y];
12340         int player_index_bit = Store2[x][y];
12341
12342         Store[x][y] = 0;
12343         Store2[x][y] = 0;
12344
12345         RemoveField(x, y);
12346         TEST_DrawLevelField(x, y);
12347
12348         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12349
12350         if (IS_ENVELOPE(element))
12351           local_player->show_envelope = element;
12352       }
12353     }
12354
12355 #if DEBUG
12356     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12357     {
12358       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12359             x, y);
12360       Debug("game:playing:GameActions_RND", "This should never happen!");
12361
12362       ChangePage[x][y] = -1;
12363     }
12364 #endif
12365
12366     Stop[x][y] = FALSE;
12367     if (WasJustMoving[x][y] > 0)
12368       WasJustMoving[x][y]--;
12369     if (WasJustFalling[x][y] > 0)
12370       WasJustFalling[x][y]--;
12371     if (CheckCollision[x][y] > 0)
12372       CheckCollision[x][y]--;
12373     if (CheckImpact[x][y] > 0)
12374       CheckImpact[x][y]--;
12375
12376     GfxFrame[x][y]++;
12377
12378     /* reset finished pushing action (not done in ContinueMoving() to allow
12379        continuous pushing animation for elements with zero push delay) */
12380     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12381     {
12382       ResetGfxAnimation(x, y);
12383       TEST_DrawLevelField(x, y);
12384     }
12385
12386 #if DEBUG
12387     if (IS_BLOCKED(x, y))
12388     {
12389       int oldx, oldy;
12390
12391       Blocked2Moving(x, y, &oldx, &oldy);
12392       if (!IS_MOVING(oldx, oldy))
12393       {
12394         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12395         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12396         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12397         Debug("game:playing:GameActions_RND", "This should never happen!");
12398       }
12399     }
12400 #endif
12401   }
12402
12403   HandleMouseAction(&mouse_action, &mouse_action_last);
12404
12405   SCAN_PLAYFIELD(x, y)
12406   {
12407     element = Tile[x][y];
12408     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12409     last_gfx_frame = GfxFrame[x][y];
12410
12411     if (element == EL_EMPTY)
12412       graphic = el2img(GfxElementEmpty[x][y]);
12413
12414     ResetGfxFrame(x, y);
12415
12416     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12417       DrawLevelGraphicAnimation(x, y, graphic);
12418
12419     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12420         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12421       ResetRandomAnimationValue(x, y);
12422
12423     SetRandomAnimationValue(x, y);
12424
12425     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12426
12427     if (IS_INACTIVE(element))
12428     {
12429       if (IS_ANIMATED(graphic))
12430         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12431
12432       continue;
12433     }
12434
12435     // this may take place after moving, so 'element' may have changed
12436     if (IS_CHANGING(x, y) &&
12437         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12438     {
12439       int page = element_info[element].event_page_nr[CE_DELAY];
12440
12441       HandleElementChange(x, y, page);
12442
12443       element = Tile[x][y];
12444       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12445     }
12446
12447     CheckNextToConditions(x, y);
12448
12449     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12450     {
12451       StartMoving(x, y);
12452
12453       element = Tile[x][y];
12454       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12455
12456       if (IS_ANIMATED(graphic) &&
12457           !IS_MOVING(x, y) &&
12458           !Stop[x][y])
12459         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12460
12461       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12462         TEST_DrawTwinkleOnField(x, y);
12463     }
12464     else if (element == EL_ACID)
12465     {
12466       if (!Stop[x][y])
12467         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12468     }
12469     else if ((element == EL_EXIT_OPEN ||
12470               element == EL_EM_EXIT_OPEN ||
12471               element == EL_SP_EXIT_OPEN ||
12472               element == EL_STEEL_EXIT_OPEN ||
12473               element == EL_EM_STEEL_EXIT_OPEN ||
12474               element == EL_SP_TERMINAL ||
12475               element == EL_SP_TERMINAL_ACTIVE ||
12476               element == EL_EXTRA_TIME ||
12477               element == EL_SHIELD_NORMAL ||
12478               element == EL_SHIELD_DEADLY) &&
12479              IS_ANIMATED(graphic))
12480       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12481     else if (IS_MOVING(x, y))
12482       ContinueMoving(x, y);
12483     else if (IS_ACTIVE_BOMB(element))
12484       CheckDynamite(x, y);
12485     else if (element == EL_AMOEBA_GROWING)
12486       AmoebaGrowing(x, y);
12487     else if (element == EL_AMOEBA_SHRINKING)
12488       AmoebaShrinking(x, y);
12489
12490 #if !USE_NEW_AMOEBA_CODE
12491     else if (IS_AMOEBALIVE(element))
12492       AmoebaReproduce(x, y);
12493 #endif
12494
12495     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12496       Life(x, y);
12497     else if (element == EL_EXIT_CLOSED)
12498       CheckExit(x, y);
12499     else if (element == EL_EM_EXIT_CLOSED)
12500       CheckExitEM(x, y);
12501     else if (element == EL_STEEL_EXIT_CLOSED)
12502       CheckExitSteel(x, y);
12503     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12504       CheckExitSteelEM(x, y);
12505     else if (element == EL_SP_EXIT_CLOSED)
12506       CheckExitSP(x, y);
12507     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12508              element == EL_EXPANDABLE_STEELWALL_GROWING)
12509       WallGrowing(x, y);
12510     else if (element == EL_EXPANDABLE_WALL ||
12511              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12512              element == EL_EXPANDABLE_WALL_VERTICAL ||
12513              element == EL_EXPANDABLE_WALL_ANY ||
12514              element == EL_BD_EXPANDABLE_WALL ||
12515              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12516              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12517              element == EL_EXPANDABLE_STEELWALL_ANY)
12518       CheckWallGrowing(x, y);
12519     else if (element == EL_FLAMES)
12520       CheckForDragon(x, y);
12521     else if (element == EL_EXPLOSION)
12522       ; // drawing of correct explosion animation is handled separately
12523     else if (element == EL_ELEMENT_SNAPPING ||
12524              element == EL_DIAGONAL_SHRINKING ||
12525              element == EL_DIAGONAL_GROWING)
12526     {
12527       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12528
12529       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12530     }
12531     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12532       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12533
12534     if (IS_BELT_ACTIVE(element))
12535       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12536
12537     if (game.magic_wall_active)
12538     {
12539       int jx = local_player->jx, jy = local_player->jy;
12540
12541       // play the element sound at the position nearest to the player
12542       if ((element == EL_MAGIC_WALL_FULL ||
12543            element == EL_MAGIC_WALL_ACTIVE ||
12544            element == EL_MAGIC_WALL_EMPTYING ||
12545            element == EL_BD_MAGIC_WALL_FULL ||
12546            element == EL_BD_MAGIC_WALL_ACTIVE ||
12547            element == EL_BD_MAGIC_WALL_EMPTYING ||
12548            element == EL_DC_MAGIC_WALL_FULL ||
12549            element == EL_DC_MAGIC_WALL_ACTIVE ||
12550            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12551           ABS(x - jx) + ABS(y - jy) <
12552           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12553       {
12554         magic_wall_x = x;
12555         magic_wall_y = y;
12556       }
12557     }
12558   }
12559
12560 #if USE_NEW_AMOEBA_CODE
12561   // new experimental amoeba growth stuff
12562   if (!(FrameCounter % 8))
12563   {
12564     static unsigned int random = 1684108901;
12565
12566     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12567     {
12568       x = RND(lev_fieldx);
12569       y = RND(lev_fieldy);
12570       element = Tile[x][y];
12571
12572       if (!IS_PLAYER(x, y) &&
12573           (element == EL_EMPTY ||
12574            CAN_GROW_INTO(element) ||
12575            element == EL_QUICKSAND_EMPTY ||
12576            element == EL_QUICKSAND_FAST_EMPTY ||
12577            element == EL_ACID_SPLASH_LEFT ||
12578            element == EL_ACID_SPLASH_RIGHT))
12579       {
12580         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12581             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12582             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12583             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12584           Tile[x][y] = EL_AMOEBA_DROP;
12585       }
12586
12587       random = random * 129 + 1;
12588     }
12589   }
12590 #endif
12591
12592   game.explosions_delayed = FALSE;
12593
12594   SCAN_PLAYFIELD(x, y)
12595   {
12596     element = Tile[x][y];
12597
12598     if (ExplodeField[x][y])
12599       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12600     else if (element == EL_EXPLOSION)
12601       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12602
12603     ExplodeField[x][y] = EX_TYPE_NONE;
12604   }
12605
12606   game.explosions_delayed = TRUE;
12607
12608   if (game.magic_wall_active)
12609   {
12610     if (!(game.magic_wall_time_left % 4))
12611     {
12612       int element = Tile[magic_wall_x][magic_wall_y];
12613
12614       if (element == EL_BD_MAGIC_WALL_FULL ||
12615           element == EL_BD_MAGIC_WALL_ACTIVE ||
12616           element == EL_BD_MAGIC_WALL_EMPTYING)
12617         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12618       else if (element == EL_DC_MAGIC_WALL_FULL ||
12619                element == EL_DC_MAGIC_WALL_ACTIVE ||
12620                element == EL_DC_MAGIC_WALL_EMPTYING)
12621         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12622       else
12623         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12624     }
12625
12626     if (game.magic_wall_time_left > 0)
12627     {
12628       game.magic_wall_time_left--;
12629
12630       if (!game.magic_wall_time_left)
12631       {
12632         SCAN_PLAYFIELD(x, y)
12633         {
12634           element = Tile[x][y];
12635
12636           if (element == EL_MAGIC_WALL_ACTIVE ||
12637               element == EL_MAGIC_WALL_FULL)
12638           {
12639             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12640             TEST_DrawLevelField(x, y);
12641           }
12642           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12643                    element == EL_BD_MAGIC_WALL_FULL)
12644           {
12645             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12646             TEST_DrawLevelField(x, y);
12647           }
12648           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12649                    element == EL_DC_MAGIC_WALL_FULL)
12650           {
12651             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12652             TEST_DrawLevelField(x, y);
12653           }
12654         }
12655
12656         game.magic_wall_active = FALSE;
12657       }
12658     }
12659   }
12660
12661   if (game.light_time_left > 0)
12662   {
12663     game.light_time_left--;
12664
12665     if (game.light_time_left == 0)
12666       RedrawAllLightSwitchesAndInvisibleElements();
12667   }
12668
12669   if (game.timegate_time_left > 0)
12670   {
12671     game.timegate_time_left--;
12672
12673     if (game.timegate_time_left == 0)
12674       CloseAllOpenTimegates();
12675   }
12676
12677   if (game.lenses_time_left > 0)
12678   {
12679     game.lenses_time_left--;
12680
12681     if (game.lenses_time_left == 0)
12682       RedrawAllInvisibleElementsForLenses();
12683   }
12684
12685   if (game.magnify_time_left > 0)
12686   {
12687     game.magnify_time_left--;
12688
12689     if (game.magnify_time_left == 0)
12690       RedrawAllInvisibleElementsForMagnifier();
12691   }
12692
12693   for (i = 0; i < MAX_PLAYERS; i++)
12694   {
12695     struct PlayerInfo *player = &stored_player[i];
12696
12697     if (SHIELD_ON(player))
12698     {
12699       if (player->shield_deadly_time_left)
12700         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12701       else if (player->shield_normal_time_left)
12702         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12703     }
12704   }
12705
12706 #if USE_DELAYED_GFX_REDRAW
12707   SCAN_PLAYFIELD(x, y)
12708   {
12709     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12710     {
12711       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12712          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12713
12714       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12715         DrawLevelField(x, y);
12716
12717       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12718         DrawLevelFieldCrumbled(x, y);
12719
12720       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12721         DrawLevelFieldCrumbledNeighbours(x, y);
12722
12723       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12724         DrawTwinkleOnField(x, y);
12725     }
12726
12727     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12728   }
12729 #endif
12730
12731   DrawAllPlayers();
12732   PlayAllPlayersSound();
12733
12734   for (i = 0; i < MAX_PLAYERS; i++)
12735   {
12736     struct PlayerInfo *player = &stored_player[i];
12737
12738     if (player->show_envelope != 0 && (!player->active ||
12739                                        player->MovPos == 0))
12740     {
12741       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12742
12743       player->show_envelope = 0;
12744     }
12745   }
12746
12747   // use random number generator in every frame to make it less predictable
12748   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12749     RND(1);
12750
12751   mouse_action_last = mouse_action;
12752 }
12753
12754 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12755 {
12756   int min_x = x, min_y = y, max_x = x, max_y = y;
12757   int scr_fieldx = getScreenFieldSizeX();
12758   int scr_fieldy = getScreenFieldSizeY();
12759   int i;
12760
12761   for (i = 0; i < MAX_PLAYERS; i++)
12762   {
12763     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12764
12765     if (!stored_player[i].active || &stored_player[i] == player)
12766       continue;
12767
12768     min_x = MIN(min_x, jx);
12769     min_y = MIN(min_y, jy);
12770     max_x = MAX(max_x, jx);
12771     max_y = MAX(max_y, jy);
12772   }
12773
12774   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12775 }
12776
12777 static boolean AllPlayersInVisibleScreen(void)
12778 {
12779   int i;
12780
12781   for (i = 0; i < MAX_PLAYERS; i++)
12782   {
12783     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12784
12785     if (!stored_player[i].active)
12786       continue;
12787
12788     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12789       return FALSE;
12790   }
12791
12792   return TRUE;
12793 }
12794
12795 void ScrollLevel(int dx, int dy)
12796 {
12797   int scroll_offset = 2 * TILEX_VAR;
12798   int x, y;
12799
12800   BlitBitmap(drawto_field, drawto_field,
12801              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12802              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12803              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12804              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12805              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12806              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12807
12808   if (dx != 0)
12809   {
12810     x = (dx == 1 ? BX1 : BX2);
12811     for (y = BY1; y <= BY2; y++)
12812       DrawScreenField(x, y);
12813   }
12814
12815   if (dy != 0)
12816   {
12817     y = (dy == 1 ? BY1 : BY2);
12818     for (x = BX1; x <= BX2; x++)
12819       DrawScreenField(x, y);
12820   }
12821
12822   redraw_mask |= REDRAW_FIELD;
12823 }
12824
12825 static boolean canFallDown(struct PlayerInfo *player)
12826 {
12827   int jx = player->jx, jy = player->jy;
12828
12829   return (IN_LEV_FIELD(jx, jy + 1) &&
12830           (IS_FREE(jx, jy + 1) ||
12831            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12832           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12833           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12834 }
12835
12836 static boolean canPassField(int x, int y, int move_dir)
12837 {
12838   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12839   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12840   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12841   int nextx = x + dx;
12842   int nexty = y + dy;
12843   int element = Tile[x][y];
12844
12845   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12846           !CAN_MOVE(element) &&
12847           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12848           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12849           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12850 }
12851
12852 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12853 {
12854   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12855   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12856   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12857   int newx = x + dx;
12858   int newy = y + dy;
12859
12860   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12861           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12862           (IS_DIGGABLE(Tile[newx][newy]) ||
12863            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12864            canPassField(newx, newy, move_dir)));
12865 }
12866
12867 static void CheckGravityMovement(struct PlayerInfo *player)
12868 {
12869   if (player->gravity && !player->programmed_action)
12870   {
12871     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12872     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12873     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12874     int jx = player->jx, jy = player->jy;
12875     boolean player_is_moving_to_valid_field =
12876       (!player_is_snapping &&
12877        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12878         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12879     boolean player_can_fall_down = canFallDown(player);
12880
12881     if (player_can_fall_down &&
12882         !player_is_moving_to_valid_field)
12883       player->programmed_action = MV_DOWN;
12884   }
12885 }
12886
12887 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12888 {
12889   return CheckGravityMovement(player);
12890
12891   if (player->gravity && !player->programmed_action)
12892   {
12893     int jx = player->jx, jy = player->jy;
12894     boolean field_under_player_is_free =
12895       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12896     boolean player_is_standing_on_valid_field =
12897       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12898        (IS_WALKABLE(Tile[jx][jy]) &&
12899         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12900
12901     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12902       player->programmed_action = MV_DOWN;
12903   }
12904 }
12905
12906 /*
12907   MovePlayerOneStep()
12908   -----------------------------------------------------------------------------
12909   dx, dy:               direction (non-diagonal) to try to move the player to
12910   real_dx, real_dy:     direction as read from input device (can be diagonal)
12911 */
12912
12913 boolean MovePlayerOneStep(struct PlayerInfo *player,
12914                           int dx, int dy, int real_dx, int real_dy)
12915 {
12916   int jx = player->jx, jy = player->jy;
12917   int new_jx = jx + dx, new_jy = jy + dy;
12918   int can_move;
12919   boolean player_can_move = !player->cannot_move;
12920
12921   if (!player->active || (!dx && !dy))
12922     return MP_NO_ACTION;
12923
12924   player->MovDir = (dx < 0 ? MV_LEFT :
12925                     dx > 0 ? MV_RIGHT :
12926                     dy < 0 ? MV_UP :
12927                     dy > 0 ? MV_DOWN :  MV_NONE);
12928
12929   if (!IN_LEV_FIELD(new_jx, new_jy))
12930     return MP_NO_ACTION;
12931
12932   if (!player_can_move)
12933   {
12934     if (player->MovPos == 0)
12935     {
12936       player->is_moving = FALSE;
12937       player->is_digging = FALSE;
12938       player->is_collecting = FALSE;
12939       player->is_snapping = FALSE;
12940       player->is_pushing = FALSE;
12941     }
12942   }
12943
12944   if (!network.enabled && game.centered_player_nr == -1 &&
12945       !AllPlayersInSight(player, new_jx, new_jy))
12946     return MP_NO_ACTION;
12947
12948   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
12949   if (can_move != MP_MOVING)
12950     return can_move;
12951
12952   // check if DigField() has caused relocation of the player
12953   if (player->jx != jx || player->jy != jy)
12954     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12955
12956   StorePlayer[jx][jy] = 0;
12957   player->last_jx = jx;
12958   player->last_jy = jy;
12959   player->jx = new_jx;
12960   player->jy = new_jy;
12961   StorePlayer[new_jx][new_jy] = player->element_nr;
12962
12963   if (player->move_delay_value_next != -1)
12964   {
12965     player->move_delay_value = player->move_delay_value_next;
12966     player->move_delay_value_next = -1;
12967   }
12968
12969   player->MovPos =
12970     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12971
12972   player->step_counter++;
12973
12974   PlayerVisit[jx][jy] = FrameCounter;
12975
12976   player->is_moving = TRUE;
12977
12978 #if 1
12979   // should better be called in MovePlayer(), but this breaks some tapes
12980   ScrollPlayer(player, SCROLL_INIT);
12981 #endif
12982
12983   return MP_MOVING;
12984 }
12985
12986 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12987 {
12988   int jx = player->jx, jy = player->jy;
12989   int old_jx = jx, old_jy = jy;
12990   int moved = MP_NO_ACTION;
12991
12992   if (!player->active)
12993     return FALSE;
12994
12995   if (!dx && !dy)
12996   {
12997     if (player->MovPos == 0)
12998     {
12999       player->is_moving = FALSE;
13000       player->is_digging = FALSE;
13001       player->is_collecting = FALSE;
13002       player->is_snapping = FALSE;
13003       player->is_pushing = FALSE;
13004     }
13005
13006     return FALSE;
13007   }
13008
13009   if (player->move_delay > 0)
13010     return FALSE;
13011
13012   player->move_delay = -1;              // set to "uninitialized" value
13013
13014   // store if player is automatically moved to next field
13015   player->is_auto_moving = (player->programmed_action != MV_NONE);
13016
13017   // remove the last programmed player action
13018   player->programmed_action = 0;
13019
13020   if (player->MovPos)
13021   {
13022     // should only happen if pre-1.2 tape recordings are played
13023     // this is only for backward compatibility
13024
13025     int original_move_delay_value = player->move_delay_value;
13026
13027 #if DEBUG
13028     Debug("game:playing:MovePlayer",
13029           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
13030           tape.counter);
13031 #endif
13032
13033     // scroll remaining steps with finest movement resolution
13034     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13035
13036     while (player->MovPos)
13037     {
13038       ScrollPlayer(player, SCROLL_GO_ON);
13039       ScrollScreen(NULL, SCROLL_GO_ON);
13040
13041       AdvanceFrameAndPlayerCounters(player->index_nr);
13042
13043       DrawAllPlayers();
13044       BackToFront_WithFrameDelay(0);
13045     }
13046
13047     player->move_delay_value = original_move_delay_value;
13048   }
13049
13050   player->is_active = FALSE;
13051
13052   if (player->last_move_dir & MV_HORIZONTAL)
13053   {
13054     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13055       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13056   }
13057   else
13058   {
13059     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13060       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13061   }
13062
13063   if (!moved && !player->is_active)
13064   {
13065     player->is_moving = FALSE;
13066     player->is_digging = FALSE;
13067     player->is_collecting = FALSE;
13068     player->is_snapping = FALSE;
13069     player->is_pushing = FALSE;
13070   }
13071
13072   jx = player->jx;
13073   jy = player->jy;
13074
13075   if (moved & MP_MOVING && !ScreenMovPos &&
13076       (player->index_nr == game.centered_player_nr ||
13077        game.centered_player_nr == -1))
13078   {
13079     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13080
13081     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13082     {
13083       // actual player has left the screen -- scroll in that direction
13084       if (jx != old_jx)         // player has moved horizontally
13085         scroll_x += (jx - old_jx);
13086       else                      // player has moved vertically
13087         scroll_y += (jy - old_jy);
13088     }
13089     else
13090     {
13091       int offset_raw = game.scroll_delay_value;
13092
13093       if (jx != old_jx)         // player has moved horizontally
13094       {
13095         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13096         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13097         int new_scroll_x = jx - MIDPOSX + offset_x;
13098
13099         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13100             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13101           scroll_x = new_scroll_x;
13102
13103         // don't scroll over playfield boundaries
13104         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13105
13106         // don't scroll more than one field at a time
13107         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13108
13109         // don't scroll against the player's moving direction
13110         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13111             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13112           scroll_x = old_scroll_x;
13113       }
13114       else                      // player has moved vertically
13115       {
13116         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13117         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13118         int new_scroll_y = jy - MIDPOSY + offset_y;
13119
13120         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13121             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13122           scroll_y = new_scroll_y;
13123
13124         // don't scroll over playfield boundaries
13125         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13126
13127         // don't scroll more than one field at a time
13128         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13129
13130         // don't scroll against the player's moving direction
13131         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13132             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13133           scroll_y = old_scroll_y;
13134       }
13135     }
13136
13137     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13138     {
13139       if (!network.enabled && game.centered_player_nr == -1 &&
13140           !AllPlayersInVisibleScreen())
13141       {
13142         scroll_x = old_scroll_x;
13143         scroll_y = old_scroll_y;
13144       }
13145       else
13146       {
13147         ScrollScreen(player, SCROLL_INIT);
13148         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13149       }
13150     }
13151   }
13152
13153   player->StepFrame = 0;
13154
13155   if (moved & MP_MOVING)
13156   {
13157     if (old_jx != jx && old_jy == jy)
13158       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13159     else if (old_jx == jx && old_jy != jy)
13160       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13161
13162     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13163
13164     player->last_move_dir = player->MovDir;
13165     player->is_moving = TRUE;
13166     player->is_snapping = FALSE;
13167     player->is_switching = FALSE;
13168     player->is_dropping = FALSE;
13169     player->is_dropping_pressed = FALSE;
13170     player->drop_pressed_delay = 0;
13171
13172 #if 0
13173     // should better be called here than above, but this breaks some tapes
13174     ScrollPlayer(player, SCROLL_INIT);
13175 #endif
13176   }
13177   else
13178   {
13179     CheckGravityMovementWhenNotMoving(player);
13180
13181     player->is_moving = FALSE;
13182
13183     /* at this point, the player is allowed to move, but cannot move right now
13184        (e.g. because of something blocking the way) -- ensure that the player
13185        is also allowed to move in the next frame (in old versions before 3.1.1,
13186        the player was forced to wait again for eight frames before next try) */
13187
13188     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13189       player->move_delay = 0;   // allow direct movement in the next frame
13190   }
13191
13192   if (player->move_delay == -1)         // not yet initialized by DigField()
13193     player->move_delay = player->move_delay_value;
13194
13195   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13196   {
13197     TestIfPlayerTouchesBadThing(jx, jy);
13198     TestIfPlayerTouchesCustomElement(jx, jy);
13199   }
13200
13201   if (!player->active)
13202     RemovePlayer(player);
13203
13204   return moved;
13205 }
13206
13207 void ScrollPlayer(struct PlayerInfo *player, int mode)
13208 {
13209   int jx = player->jx, jy = player->jy;
13210   int last_jx = player->last_jx, last_jy = player->last_jy;
13211   int move_stepsize = TILEX / player->move_delay_value;
13212
13213   if (!player->active)
13214     return;
13215
13216   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13217     return;
13218
13219   if (mode == SCROLL_INIT)
13220   {
13221     player->actual_frame_counter.count = FrameCounter;
13222     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13223
13224     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13225         Tile[last_jx][last_jy] == EL_EMPTY)
13226     {
13227       int last_field_block_delay = 0;   // start with no blocking at all
13228       int block_delay_adjustment = player->block_delay_adjustment;
13229
13230       // if player blocks last field, add delay for exactly one move
13231       if (player->block_last_field)
13232       {
13233         last_field_block_delay += player->move_delay_value;
13234
13235         // when blocking enabled, prevent moving up despite gravity
13236         if (player->gravity && player->MovDir == MV_UP)
13237           block_delay_adjustment = -1;
13238       }
13239
13240       // add block delay adjustment (also possible when not blocking)
13241       last_field_block_delay += block_delay_adjustment;
13242
13243       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13244       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13245     }
13246
13247     if (player->MovPos != 0)    // player has not yet reached destination
13248       return;
13249   }
13250   else if (!FrameReached(&player->actual_frame_counter))
13251     return;
13252
13253   if (player->MovPos != 0)
13254   {
13255     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13256     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13257
13258     // before DrawPlayer() to draw correct player graphic for this case
13259     if (player->MovPos == 0)
13260       CheckGravityMovement(player);
13261   }
13262
13263   if (player->MovPos == 0)      // player reached destination field
13264   {
13265     if (player->move_delay_reset_counter > 0)
13266     {
13267       player->move_delay_reset_counter--;
13268
13269       if (player->move_delay_reset_counter == 0)
13270       {
13271         // continue with normal speed after quickly moving through gate
13272         HALVE_PLAYER_SPEED(player);
13273
13274         // be able to make the next move without delay
13275         player->move_delay = 0;
13276       }
13277     }
13278
13279     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13280         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13281         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13282         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13283         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13284         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13285         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13286         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13287     {
13288       ExitPlayer(player);
13289
13290       if (game.players_still_needed == 0 &&
13291           (game.friends_still_needed == 0 ||
13292            IS_SP_ELEMENT(Tile[jx][jy])))
13293         LevelSolved();
13294     }
13295
13296     player->last_jx = jx;
13297     player->last_jy = jy;
13298
13299     // this breaks one level: "machine", level 000
13300     {
13301       int move_direction = player->MovDir;
13302       int enter_side = MV_DIR_OPPOSITE(move_direction);
13303       int leave_side = move_direction;
13304       int old_jx = last_jx;
13305       int old_jy = last_jy;
13306       int old_element = Tile[old_jx][old_jy];
13307       int new_element = Tile[jx][jy];
13308
13309       if (IS_CUSTOM_ELEMENT(old_element))
13310         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13311                                    CE_LEFT_BY_PLAYER,
13312                                    player->index_bit, leave_side);
13313
13314       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13315                                           CE_PLAYER_LEAVES_X,
13316                                           player->index_bit, leave_side);
13317
13318       // needed because pushed element has not yet reached its destination,
13319       // so it would trigger a change event at its previous field location
13320       if (!player->is_pushing)
13321       {
13322         if (IS_CUSTOM_ELEMENT(new_element))
13323           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13324                                      player->index_bit, enter_side);
13325
13326         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13327                                             CE_PLAYER_ENTERS_X,
13328                                             player->index_bit, enter_side);
13329       }
13330
13331       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13332                                         CE_MOVE_OF_X, move_direction);
13333     }
13334
13335     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13336     {
13337       TestIfPlayerTouchesBadThing(jx, jy);
13338       TestIfPlayerTouchesCustomElement(jx, jy);
13339
13340       // needed because pushed element has not yet reached its destination,
13341       // so it would trigger a change event at its previous field location
13342       if (!player->is_pushing)
13343         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13344
13345       if (level.finish_dig_collect &&
13346           (player->is_digging || player->is_collecting))
13347       {
13348         int last_element = player->last_removed_element;
13349         int move_direction = player->MovDir;
13350         int enter_side = MV_DIR_OPPOSITE(move_direction);
13351         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13352                             CE_PLAYER_COLLECTS_X);
13353
13354         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13355                                             player->index_bit, enter_side);
13356
13357         player->last_removed_element = EL_UNDEFINED;
13358       }
13359
13360       if (!player->active)
13361         RemovePlayer(player);
13362     }
13363
13364     if (level.use_step_counter)
13365       CheckLevelTime_StepCounter();
13366
13367     if (tape.single_step && tape.recording && !tape.pausing &&
13368         !player->programmed_action)
13369       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13370
13371     if (!player->programmed_action)
13372       CheckSaveEngineSnapshot(player);
13373   }
13374 }
13375
13376 void ScrollScreen(struct PlayerInfo *player, int mode)
13377 {
13378   static DelayCounter screen_frame_counter = { 0 };
13379
13380   if (mode == SCROLL_INIT)
13381   {
13382     // set scrolling step size according to actual player's moving speed
13383     ScrollStepSize = TILEX / player->move_delay_value;
13384
13385     screen_frame_counter.count = FrameCounter;
13386     screen_frame_counter.value = 1;
13387
13388     ScreenMovDir = player->MovDir;
13389     ScreenMovPos = player->MovPos;
13390     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13391     return;
13392   }
13393   else if (!FrameReached(&screen_frame_counter))
13394     return;
13395
13396   if (ScreenMovPos)
13397   {
13398     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13399     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13400     redraw_mask |= REDRAW_FIELD;
13401   }
13402   else
13403     ScreenMovDir = MV_NONE;
13404 }
13405
13406 void CheckNextToConditions(int x, int y)
13407 {
13408   int element = Tile[x][y];
13409
13410   if (IS_PLAYER(x, y))
13411     TestIfPlayerNextToCustomElement(x, y);
13412
13413   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13414       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13415     TestIfElementNextToCustomElement(x, y);
13416 }
13417
13418 void TestIfPlayerNextToCustomElement(int x, int y)
13419 {
13420   struct XY *xy = xy_topdown;
13421   static int trigger_sides[4][2] =
13422   {
13423     // center side       border side
13424     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13425     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13426     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13427     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13428   };
13429   int i;
13430
13431   if (!IS_PLAYER(x, y))
13432     return;
13433
13434   struct PlayerInfo *player = PLAYERINFO(x, y);
13435
13436   if (player->is_moving)
13437     return;
13438
13439   for (i = 0; i < NUM_DIRECTIONS; i++)
13440   {
13441     int xx = x + xy[i].x;
13442     int yy = y + xy[i].y;
13443     int border_side = trigger_sides[i][1];
13444     int border_element;
13445
13446     if (!IN_LEV_FIELD(xx, yy))
13447       continue;
13448
13449     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13450       continue;         // center and border element not connected
13451
13452     border_element = Tile[xx][yy];
13453
13454     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13455                                player->index_bit, border_side);
13456     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13457                                         CE_PLAYER_NEXT_TO_X,
13458                                         player->index_bit, border_side);
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
13465     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13466                              CE_NEXT_TO_X, border_side);
13467   }
13468 }
13469
13470 void TestIfPlayerTouchesCustomElement(int x, int y)
13471 {
13472   struct XY *xy = xy_topdown;
13473   static int trigger_sides[4][2] =
13474   {
13475     // center side       border side
13476     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13477     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13478     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13479     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13480   };
13481   static int touch_dir[4] =
13482   {
13483     MV_LEFT | MV_RIGHT,
13484     MV_UP   | MV_DOWN,
13485     MV_UP   | MV_DOWN,
13486     MV_LEFT | MV_RIGHT
13487   };
13488   int center_element = Tile[x][y];      // should always be non-moving!
13489   int i;
13490
13491   for (i = 0; i < NUM_DIRECTIONS; i++)
13492   {
13493     int xx = x + xy[i].x;
13494     int yy = y + xy[i].y;
13495     int center_side = trigger_sides[i][0];
13496     int border_side = trigger_sides[i][1];
13497     int border_element;
13498
13499     if (!IN_LEV_FIELD(xx, yy))
13500       continue;
13501
13502     if (IS_PLAYER(x, y))                // player found at center element
13503     {
13504       struct PlayerInfo *player = PLAYERINFO(x, y);
13505
13506       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13507         border_element = Tile[xx][yy];          // may be moving!
13508       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13509         border_element = Tile[xx][yy];
13510       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13511         border_element = MovingOrBlocked2Element(xx, yy);
13512       else
13513         continue;               // center and border element do not touch
13514
13515       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13516                                  player->index_bit, border_side);
13517       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13518                                           CE_PLAYER_TOUCHES_X,
13519                                           player->index_bit, border_side);
13520
13521       {
13522         /* use player element that is initially defined in the level playfield,
13523            not the player element that corresponds to the runtime player number
13524            (example: a level that contains EL_PLAYER_3 as the only player would
13525            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13526         int player_element = PLAYERINFO(x, y)->initial_element;
13527
13528         // as element "X" is the player here, check opposite (center) side
13529         CheckElementChangeBySide(xx, yy, border_element, player_element,
13530                                  CE_TOUCHING_X, center_side);
13531       }
13532     }
13533     else if (IS_PLAYER(xx, yy))         // player found at border element
13534     {
13535       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13536
13537       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13538       {
13539         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13540           continue;             // center and border element do not touch
13541       }
13542
13543       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13544                                  player->index_bit, center_side);
13545       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13546                                           CE_PLAYER_TOUCHES_X,
13547                                           player->index_bit, center_side);
13548
13549       {
13550         /* use player element that is initially defined in the level playfield,
13551            not the player element that corresponds to the runtime player number
13552            (example: a level that contains EL_PLAYER_3 as the only player would
13553            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13554         int player_element = PLAYERINFO(xx, yy)->initial_element;
13555
13556         // as element "X" is the player here, check opposite (border) side
13557         CheckElementChangeBySide(x, y, center_element, player_element,
13558                                  CE_TOUCHING_X, border_side);
13559       }
13560
13561       break;
13562     }
13563   }
13564 }
13565
13566 void TestIfElementNextToCustomElement(int x, int y)
13567 {
13568   struct XY *xy = xy_topdown;
13569   static int trigger_sides[4][2] =
13570   {
13571     // center side      border side
13572     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13573     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13574     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13575     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13576   };
13577   int center_element = Tile[x][y];      // should always be non-moving!
13578   int i;
13579
13580   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13581     return;
13582
13583   for (i = 0; i < NUM_DIRECTIONS; i++)
13584   {
13585     int xx = x + xy[i].x;
13586     int yy = y + xy[i].y;
13587     int border_side = trigger_sides[i][1];
13588     int border_element;
13589
13590     if (!IN_LEV_FIELD(xx, yy))
13591       continue;
13592
13593     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13594       continue;                 // center and border element not connected
13595
13596     border_element = Tile[xx][yy];
13597
13598     // check for change of center element (but change it only once)
13599     if (CheckElementChangeBySide(x, y, center_element, border_element,
13600                                  CE_NEXT_TO_X, border_side))
13601       break;
13602   }
13603 }
13604
13605 void TestIfElementTouchesCustomElement(int x, int y)
13606 {
13607   struct XY *xy = xy_topdown;
13608   static int trigger_sides[4][2] =
13609   {
13610     // center side      border side
13611     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13612     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13613     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13614     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13615   };
13616   static int touch_dir[4] =
13617   {
13618     MV_LEFT | MV_RIGHT,
13619     MV_UP   | MV_DOWN,
13620     MV_UP   | MV_DOWN,
13621     MV_LEFT | MV_RIGHT
13622   };
13623   boolean change_center_element = FALSE;
13624   int center_element = Tile[x][y];      // should always be non-moving!
13625   int border_element_old[NUM_DIRECTIONS];
13626   int i;
13627
13628   for (i = 0; i < NUM_DIRECTIONS; i++)
13629   {
13630     int xx = x + xy[i].x;
13631     int yy = y + xy[i].y;
13632     int border_element;
13633
13634     border_element_old[i] = -1;
13635
13636     if (!IN_LEV_FIELD(xx, yy))
13637       continue;
13638
13639     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13640       border_element = Tile[xx][yy];    // may be moving!
13641     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13642       border_element = Tile[xx][yy];
13643     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13644       border_element = MovingOrBlocked2Element(xx, yy);
13645     else
13646       continue;                 // center and border element do not touch
13647
13648     border_element_old[i] = border_element;
13649   }
13650
13651   for (i = 0; i < NUM_DIRECTIONS; i++)
13652   {
13653     int xx = x + xy[i].x;
13654     int yy = y + xy[i].y;
13655     int center_side = trigger_sides[i][0];
13656     int border_element = border_element_old[i];
13657
13658     if (border_element == -1)
13659       continue;
13660
13661     // check for change of border element
13662     CheckElementChangeBySide(xx, yy, border_element, center_element,
13663                              CE_TOUCHING_X, center_side);
13664
13665     // (center element cannot be player, so we don't have to check this here)
13666   }
13667
13668   for (i = 0; i < NUM_DIRECTIONS; i++)
13669   {
13670     int xx = x + xy[i].x;
13671     int yy = y + xy[i].y;
13672     int border_side = trigger_sides[i][1];
13673     int border_element = border_element_old[i];
13674
13675     if (border_element == -1)
13676       continue;
13677
13678     // check for change of center element (but change it only once)
13679     if (!change_center_element)
13680       change_center_element =
13681         CheckElementChangeBySide(x, y, center_element, border_element,
13682                                  CE_TOUCHING_X, border_side);
13683
13684     if (IS_PLAYER(xx, yy))
13685     {
13686       /* use player element that is initially defined in the level playfield,
13687          not the player element that corresponds to the runtime player number
13688          (example: a level that contains EL_PLAYER_3 as the only player would
13689          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13690       int player_element = PLAYERINFO(xx, yy)->initial_element;
13691
13692       // as element "X" is the player here, check opposite (border) side
13693       CheckElementChangeBySide(x, y, center_element, player_element,
13694                                CE_TOUCHING_X, border_side);
13695     }
13696   }
13697 }
13698
13699 void TestIfElementHitsCustomElement(int x, int y, int direction)
13700 {
13701   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13702   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13703   int hitx = x + dx, hity = y + dy;
13704   int hitting_element = Tile[x][y];
13705   int touched_element;
13706
13707   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13708     return;
13709
13710   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13711                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13712
13713   if (IN_LEV_FIELD(hitx, hity))
13714   {
13715     int opposite_direction = MV_DIR_OPPOSITE(direction);
13716     int hitting_side = direction;
13717     int touched_side = opposite_direction;
13718     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13719                           MovDir[hitx][hity] != direction ||
13720                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13721
13722     object_hit = TRUE;
13723
13724     if (object_hit)
13725     {
13726       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13727                                CE_HITTING_X, touched_side);
13728
13729       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13730                                CE_HIT_BY_X, hitting_side);
13731
13732       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13733                                CE_HIT_BY_SOMETHING, opposite_direction);
13734
13735       if (IS_PLAYER(hitx, hity))
13736       {
13737         /* use player element that is initially defined in the level playfield,
13738            not the player element that corresponds to the runtime player number
13739            (example: a level that contains EL_PLAYER_3 as the only player would
13740            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13741         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13742
13743         CheckElementChangeBySide(x, y, hitting_element, player_element,
13744                                  CE_HITTING_X, touched_side);
13745       }
13746     }
13747   }
13748
13749   // "hitting something" is also true when hitting the playfield border
13750   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13751                            CE_HITTING_SOMETHING, direction);
13752 }
13753
13754 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13755 {
13756   int i, kill_x = -1, kill_y = -1;
13757
13758   int bad_element = -1;
13759   struct XY *test_xy = xy_topdown;
13760   static int test_dir[4] =
13761   {
13762     MV_UP,
13763     MV_LEFT,
13764     MV_RIGHT,
13765     MV_DOWN
13766   };
13767
13768   for (i = 0; i < NUM_DIRECTIONS; i++)
13769   {
13770     int test_x, test_y, test_move_dir, test_element;
13771
13772     test_x = good_x + test_xy[i].x;
13773     test_y = good_y + test_xy[i].y;
13774
13775     if (!IN_LEV_FIELD(test_x, test_y))
13776       continue;
13777
13778     test_move_dir =
13779       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13780
13781     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13782
13783     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13784        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13785     */
13786     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13787         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13788     {
13789       kill_x = test_x;
13790       kill_y = test_y;
13791       bad_element = test_element;
13792
13793       break;
13794     }
13795   }
13796
13797   if (kill_x != -1 || kill_y != -1)
13798   {
13799     if (IS_PLAYER(good_x, good_y))
13800     {
13801       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13802
13803       if (player->shield_deadly_time_left > 0 &&
13804           !IS_INDESTRUCTIBLE(bad_element))
13805         Bang(kill_x, kill_y);
13806       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13807         KillPlayer(player);
13808     }
13809     else
13810       Bang(good_x, good_y);
13811   }
13812 }
13813
13814 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13815 {
13816   int i, kill_x = -1, kill_y = -1;
13817   int bad_element = Tile[bad_x][bad_y];
13818   struct XY *test_xy = xy_topdown;
13819   static int touch_dir[4] =
13820   {
13821     MV_LEFT | MV_RIGHT,
13822     MV_UP   | MV_DOWN,
13823     MV_UP   | MV_DOWN,
13824     MV_LEFT | MV_RIGHT
13825   };
13826   static int test_dir[4] =
13827   {
13828     MV_UP,
13829     MV_LEFT,
13830     MV_RIGHT,
13831     MV_DOWN
13832   };
13833
13834   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13835     return;
13836
13837   for (i = 0; i < NUM_DIRECTIONS; i++)
13838   {
13839     int test_x, test_y, test_move_dir, test_element;
13840
13841     test_x = bad_x + test_xy[i].x;
13842     test_y = bad_y + test_xy[i].y;
13843
13844     if (!IN_LEV_FIELD(test_x, test_y))
13845       continue;
13846
13847     test_move_dir =
13848       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13849
13850     test_element = Tile[test_x][test_y];
13851
13852     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13853        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13854     */
13855     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13856         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13857     {
13858       // good thing is player or penguin that does not move away
13859       if (IS_PLAYER(test_x, test_y))
13860       {
13861         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13862
13863         if (bad_element == EL_ROBOT && player->is_moving)
13864           continue;     // robot does not kill player if he is moving
13865
13866         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13867         {
13868           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13869             continue;           // center and border element do not touch
13870         }
13871
13872         kill_x = test_x;
13873         kill_y = test_y;
13874
13875         break;
13876       }
13877       else if (test_element == EL_PENGUIN)
13878       {
13879         kill_x = test_x;
13880         kill_y = test_y;
13881
13882         break;
13883       }
13884     }
13885   }
13886
13887   if (kill_x != -1 || kill_y != -1)
13888   {
13889     if (IS_PLAYER(kill_x, kill_y))
13890     {
13891       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13892
13893       if (player->shield_deadly_time_left > 0 &&
13894           !IS_INDESTRUCTIBLE(bad_element))
13895         Bang(bad_x, bad_y);
13896       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13897         KillPlayer(player);
13898     }
13899     else
13900       Bang(kill_x, kill_y);
13901   }
13902 }
13903
13904 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13905 {
13906   int bad_element = Tile[bad_x][bad_y];
13907   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13908   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13909   int test_x = bad_x + dx, test_y = bad_y + dy;
13910   int test_move_dir, test_element;
13911   int kill_x = -1, kill_y = -1;
13912
13913   if (!IN_LEV_FIELD(test_x, test_y))
13914     return;
13915
13916   test_move_dir =
13917     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13918
13919   test_element = Tile[test_x][test_y];
13920
13921   if (test_move_dir != bad_move_dir)
13922   {
13923     // good thing can be player or penguin that does not move away
13924     if (IS_PLAYER(test_x, test_y))
13925     {
13926       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13927
13928       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13929          player as being hit when he is moving towards the bad thing, because
13930          the "get hit by" condition would be lost after the player stops) */
13931       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13932         return;         // player moves away from bad thing
13933
13934       kill_x = test_x;
13935       kill_y = test_y;
13936     }
13937     else if (test_element == EL_PENGUIN)
13938     {
13939       kill_x = test_x;
13940       kill_y = test_y;
13941     }
13942   }
13943
13944   if (kill_x != -1 || kill_y != -1)
13945   {
13946     if (IS_PLAYER(kill_x, kill_y))
13947     {
13948       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13949
13950       if (player->shield_deadly_time_left > 0 &&
13951           !IS_INDESTRUCTIBLE(bad_element))
13952         Bang(bad_x, bad_y);
13953       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13954         KillPlayer(player);
13955     }
13956     else
13957       Bang(kill_x, kill_y);
13958   }
13959 }
13960
13961 void TestIfPlayerTouchesBadThing(int x, int y)
13962 {
13963   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13964 }
13965
13966 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13967 {
13968   TestIfGoodThingHitsBadThing(x, y, move_dir);
13969 }
13970
13971 void TestIfBadThingTouchesPlayer(int x, int y)
13972 {
13973   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13974 }
13975
13976 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13977 {
13978   TestIfBadThingHitsGoodThing(x, y, move_dir);
13979 }
13980
13981 void TestIfFriendTouchesBadThing(int x, int y)
13982 {
13983   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13984 }
13985
13986 void TestIfBadThingTouchesFriend(int x, int y)
13987 {
13988   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13989 }
13990
13991 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13992 {
13993   int i, kill_x = bad_x, kill_y = bad_y;
13994   struct XY *xy = xy_topdown;
13995
13996   for (i = 0; i < NUM_DIRECTIONS; i++)
13997   {
13998     int x, y, element;
13999
14000     x = bad_x + xy[i].x;
14001     y = bad_y + xy[i].y;
14002     if (!IN_LEV_FIELD(x, y))
14003       continue;
14004
14005     element = Tile[x][y];
14006     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14007         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14008     {
14009       kill_x = x;
14010       kill_y = y;
14011       break;
14012     }
14013   }
14014
14015   if (kill_x != bad_x || kill_y != bad_y)
14016     Bang(bad_x, bad_y);
14017 }
14018
14019 void KillPlayer(struct PlayerInfo *player)
14020 {
14021   int jx = player->jx, jy = player->jy;
14022
14023   if (!player->active)
14024     return;
14025
14026 #if 0
14027   Debug("game:playing:KillPlayer",
14028         "0: killed == %d, active == %d, reanimated == %d",
14029         player->killed, player->active, player->reanimated);
14030 #endif
14031
14032   /* the following code was introduced to prevent an infinite loop when calling
14033      -> Bang()
14034      -> CheckTriggeredElementChangeExt()
14035      -> ExecuteCustomElementAction()
14036      -> KillPlayer()
14037      -> (infinitely repeating the above sequence of function calls)
14038      which occurs when killing the player while having a CE with the setting
14039      "kill player X when explosion of <player X>"; the solution using a new
14040      field "player->killed" was chosen for backwards compatibility, although
14041      clever use of the fields "player->active" etc. would probably also work */
14042 #if 1
14043   if (player->killed)
14044     return;
14045 #endif
14046
14047   player->killed = TRUE;
14048
14049   // remove accessible field at the player's position
14050   RemoveField(jx, jy);
14051
14052   // deactivate shield (else Bang()/Explode() would not work right)
14053   player->shield_normal_time_left = 0;
14054   player->shield_deadly_time_left = 0;
14055
14056 #if 0
14057   Debug("game:playing:KillPlayer",
14058         "1: killed == %d, active == %d, reanimated == %d",
14059         player->killed, player->active, player->reanimated);
14060 #endif
14061
14062   Bang(jx, jy);
14063
14064 #if 0
14065   Debug("game:playing:KillPlayer",
14066         "2: killed == %d, active == %d, reanimated == %d",
14067         player->killed, player->active, player->reanimated);
14068 #endif
14069
14070   if (player->reanimated)       // killed player may have been reanimated
14071     player->killed = player->reanimated = FALSE;
14072   else
14073     BuryPlayer(player);
14074 }
14075
14076 static void KillPlayerUnlessEnemyProtected(int x, int y)
14077 {
14078   if (!PLAYER_ENEMY_PROTECTED(x, y))
14079     KillPlayer(PLAYERINFO(x, y));
14080 }
14081
14082 static void KillPlayerUnlessExplosionProtected(int x, int y)
14083 {
14084   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14085     KillPlayer(PLAYERINFO(x, y));
14086 }
14087
14088 void BuryPlayer(struct PlayerInfo *player)
14089 {
14090   int jx = player->jx, jy = player->jy;
14091
14092   if (!player->active)
14093     return;
14094
14095   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14096
14097   RemovePlayer(player);
14098
14099   player->buried = TRUE;
14100
14101   if (game.all_players_gone)
14102     game.GameOver = TRUE;
14103 }
14104
14105 void RemovePlayer(struct PlayerInfo *player)
14106 {
14107   int jx = player->jx, jy = player->jy;
14108   int i, found = FALSE;
14109
14110   player->present = FALSE;
14111   player->active = FALSE;
14112
14113   // required for some CE actions (even if the player is not active anymore)
14114   player->MovPos = 0;
14115
14116   if (!ExplodeField[jx][jy])
14117     StorePlayer[jx][jy] = 0;
14118
14119   if (player->is_moving)
14120     TEST_DrawLevelField(player->last_jx, player->last_jy);
14121
14122   for (i = 0; i < MAX_PLAYERS; i++)
14123     if (stored_player[i].active)
14124       found = TRUE;
14125
14126   if (!found)
14127   {
14128     game.all_players_gone = TRUE;
14129     game.GameOver = TRUE;
14130   }
14131
14132   game.exit_x = game.robot_wheel_x = jx;
14133   game.exit_y = game.robot_wheel_y = jy;
14134 }
14135
14136 void ExitPlayer(struct PlayerInfo *player)
14137 {
14138   DrawPlayer(player);   // needed here only to cleanup last field
14139   RemovePlayer(player);
14140
14141   if (game.players_still_needed > 0)
14142     game.players_still_needed--;
14143 }
14144
14145 static void SetFieldForSnapping(int x, int y, int element, int direction,
14146                                 int player_index_bit)
14147 {
14148   struct ElementInfo *ei = &element_info[element];
14149   int direction_bit = MV_DIR_TO_BIT(direction);
14150   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14151   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14152                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14153
14154   Tile[x][y] = EL_ELEMENT_SNAPPING;
14155   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14156   MovDir[x][y] = direction;
14157   Store[x][y] = element;
14158   Store2[x][y] = player_index_bit;
14159
14160   ResetGfxAnimation(x, y);
14161
14162   GfxElement[x][y] = element;
14163   GfxAction[x][y] = action;
14164   GfxDir[x][y] = direction;
14165   GfxFrame[x][y] = -1;
14166 }
14167
14168 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14169                                    int player_index_bit)
14170 {
14171   TestIfElementTouchesCustomElement(x, y);      // for empty space
14172
14173   if (level.finish_dig_collect)
14174   {
14175     int dig_side = MV_DIR_OPPOSITE(direction);
14176     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14177                         CE_PLAYER_COLLECTS_X);
14178
14179     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14180                                         player_index_bit, dig_side);
14181     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14182                                         player_index_bit, dig_side);
14183   }
14184 }
14185
14186 /*
14187   =============================================================================
14188   checkDiagonalPushing()
14189   -----------------------------------------------------------------------------
14190   check if diagonal input device direction results in pushing of object
14191   (by checking if the alternative direction is walkable, diggable, ...)
14192   =============================================================================
14193 */
14194
14195 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14196                                     int x, int y, int real_dx, int real_dy)
14197 {
14198   int jx, jy, dx, dy, xx, yy;
14199
14200   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14201     return TRUE;
14202
14203   // diagonal direction: check alternative direction
14204   jx = player->jx;
14205   jy = player->jy;
14206   dx = x - jx;
14207   dy = y - jy;
14208   xx = jx + (dx == 0 ? real_dx : 0);
14209   yy = jy + (dy == 0 ? real_dy : 0);
14210
14211   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14212 }
14213
14214 /*
14215   =============================================================================
14216   DigField()
14217   -----------------------------------------------------------------------------
14218   x, y:                 field next to player (non-diagonal) to try to dig to
14219   real_dx, real_dy:     direction as read from input device (can be diagonal)
14220   =============================================================================
14221 */
14222
14223 static int DigField(struct PlayerInfo *player,
14224                     int oldx, int oldy, int x, int y,
14225                     int real_dx, int real_dy, int mode)
14226 {
14227   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14228   boolean player_was_pushing = player->is_pushing;
14229   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14230   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14231   int jx = oldx, jy = oldy;
14232   int dx = x - jx, dy = y - jy;
14233   int nextx = x + dx, nexty = y + dy;
14234   int move_direction = (dx == -1 ? MV_LEFT  :
14235                         dx == +1 ? MV_RIGHT :
14236                         dy == -1 ? MV_UP    :
14237                         dy == +1 ? MV_DOWN  : MV_NONE);
14238   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14239   int dig_side = MV_DIR_OPPOSITE(move_direction);
14240   int old_element = Tile[jx][jy];
14241   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14242   int collect_count;
14243
14244   if (is_player)                // function can also be called by EL_PENGUIN
14245   {
14246     if (player->MovPos == 0)
14247     {
14248       player->is_digging = FALSE;
14249       player->is_collecting = FALSE;
14250     }
14251
14252     if (player->MovPos == 0)    // last pushing move finished
14253       player->is_pushing = FALSE;
14254
14255     if (mode == DF_NO_PUSH)     // player just stopped pushing
14256     {
14257       player->is_switching = FALSE;
14258       player->push_delay = -1;
14259
14260       return MP_NO_ACTION;
14261     }
14262   }
14263   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14264     old_element = Back[jx][jy];
14265
14266   // in case of element dropped at player position, check background
14267   else if (Back[jx][jy] != EL_EMPTY &&
14268            game.engine_version >= VERSION_IDENT(2,2,0,0))
14269     old_element = Back[jx][jy];
14270
14271   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14272     return MP_NO_ACTION;        // field has no opening in this direction
14273
14274   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14275     return MP_NO_ACTION;        // field has no opening in this direction
14276
14277   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14278   {
14279     SplashAcid(x, y);
14280
14281     Tile[jx][jy] = player->artwork_element;
14282     InitMovingField(jx, jy, MV_DOWN);
14283     Store[jx][jy] = EL_ACID;
14284     ContinueMoving(jx, jy);
14285     BuryPlayer(player);
14286
14287     return MP_DONT_RUN_INTO;
14288   }
14289
14290   if (player_can_move && DONT_RUN_INTO(element))
14291   {
14292     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14293
14294     return MP_DONT_RUN_INTO;
14295   }
14296
14297   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14298     return MP_NO_ACTION;
14299
14300   collect_count = element_info[element].collect_count_initial;
14301
14302   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14303     return MP_NO_ACTION;
14304
14305   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14306     player_can_move = player_can_move_or_snap;
14307
14308   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14309       game.engine_version >= VERSION_IDENT(2,2,0,0))
14310   {
14311     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14312                                player->index_bit, dig_side);
14313     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14314                                         player->index_bit, dig_side);
14315
14316     if (element == EL_DC_LANDMINE)
14317       Bang(x, y);
14318
14319     if (Tile[x][y] != element)          // field changed by snapping
14320       return MP_ACTION;
14321
14322     return MP_NO_ACTION;
14323   }
14324
14325   if (player->gravity && is_player && !player->is_auto_moving &&
14326       canFallDown(player) && move_direction != MV_DOWN &&
14327       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14328     return MP_NO_ACTION;        // player cannot walk here due to gravity
14329
14330   if (player_can_move &&
14331       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14332   {
14333     int sound_element = SND_ELEMENT(element);
14334     int sound_action = ACTION_WALKING;
14335
14336     if (IS_RND_GATE(element))
14337     {
14338       if (!player->key[RND_GATE_NR(element)])
14339         return MP_NO_ACTION;
14340     }
14341     else if (IS_RND_GATE_GRAY(element))
14342     {
14343       if (!player->key[RND_GATE_GRAY_NR(element)])
14344         return MP_NO_ACTION;
14345     }
14346     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14347     {
14348       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14349         return MP_NO_ACTION;
14350     }
14351     else if (element == EL_EXIT_OPEN ||
14352              element == EL_EM_EXIT_OPEN ||
14353              element == EL_EM_EXIT_OPENING ||
14354              element == EL_STEEL_EXIT_OPEN ||
14355              element == EL_EM_STEEL_EXIT_OPEN ||
14356              element == EL_EM_STEEL_EXIT_OPENING ||
14357              element == EL_SP_EXIT_OPEN ||
14358              element == EL_SP_EXIT_OPENING)
14359     {
14360       sound_action = ACTION_PASSING;    // player is passing exit
14361     }
14362     else if (element == EL_EMPTY)
14363     {
14364       sound_action = ACTION_MOVING;             // nothing to walk on
14365     }
14366
14367     // play sound from background or player, whatever is available
14368     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14369       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14370     else
14371       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14372   }
14373   else if (player_can_move &&
14374            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14375   {
14376     if (!ACCESS_FROM(element, opposite_direction))
14377       return MP_NO_ACTION;      // field not accessible from this direction
14378
14379     if (CAN_MOVE(element))      // only fixed elements can be passed!
14380       return MP_NO_ACTION;
14381
14382     if (IS_EM_GATE(element))
14383     {
14384       if (!player->key[EM_GATE_NR(element)])
14385         return MP_NO_ACTION;
14386     }
14387     else if (IS_EM_GATE_GRAY(element))
14388     {
14389       if (!player->key[EM_GATE_GRAY_NR(element)])
14390         return MP_NO_ACTION;
14391     }
14392     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14393     {
14394       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14395         return MP_NO_ACTION;
14396     }
14397     else if (IS_EMC_GATE(element))
14398     {
14399       if (!player->key[EMC_GATE_NR(element)])
14400         return MP_NO_ACTION;
14401     }
14402     else if (IS_EMC_GATE_GRAY(element))
14403     {
14404       if (!player->key[EMC_GATE_GRAY_NR(element)])
14405         return MP_NO_ACTION;
14406     }
14407     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14408     {
14409       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14410         return MP_NO_ACTION;
14411     }
14412     else if (element == EL_DC_GATE_WHITE ||
14413              element == EL_DC_GATE_WHITE_GRAY ||
14414              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14415     {
14416       if (player->num_white_keys == 0)
14417         return MP_NO_ACTION;
14418
14419       player->num_white_keys--;
14420     }
14421     else if (IS_SP_PORT(element))
14422     {
14423       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14424           element == EL_SP_GRAVITY_PORT_RIGHT ||
14425           element == EL_SP_GRAVITY_PORT_UP ||
14426           element == EL_SP_GRAVITY_PORT_DOWN)
14427         player->gravity = !player->gravity;
14428       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14429                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14430                element == EL_SP_GRAVITY_ON_PORT_UP ||
14431                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14432         player->gravity = TRUE;
14433       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14434                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14435                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14436                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14437         player->gravity = FALSE;
14438     }
14439
14440     // automatically move to the next field with double speed
14441     player->programmed_action = move_direction;
14442
14443     if (player->move_delay_reset_counter == 0)
14444     {
14445       player->move_delay_reset_counter = 2;     // two double speed steps
14446
14447       DOUBLE_PLAYER_SPEED(player);
14448     }
14449
14450     PlayLevelSoundAction(x, y, ACTION_PASSING);
14451   }
14452   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14453   {
14454     RemoveField(x, y);
14455
14456     if (mode != DF_SNAP)
14457     {
14458       GfxElement[x][y] = GFX_ELEMENT(element);
14459       player->is_digging = TRUE;
14460     }
14461
14462     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14463
14464     // use old behaviour for old levels (digging)
14465     if (!level.finish_dig_collect)
14466     {
14467       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14468                                           player->index_bit, dig_side);
14469
14470       // if digging triggered player relocation, finish digging tile
14471       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14472         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14473     }
14474
14475     if (mode == DF_SNAP)
14476     {
14477       if (level.block_snap_field)
14478         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14479       else
14480         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14481
14482       // use old behaviour for old levels (snapping)
14483       if (!level.finish_dig_collect)
14484         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14485                                             player->index_bit, dig_side);
14486     }
14487   }
14488   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14489   {
14490     RemoveField(x, y);
14491
14492     if (is_player && mode != DF_SNAP)
14493     {
14494       GfxElement[x][y] = element;
14495       player->is_collecting = TRUE;
14496     }
14497
14498     if (element == EL_SPEED_PILL)
14499     {
14500       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14501     }
14502     else if (element == EL_EXTRA_TIME && level.time > 0)
14503     {
14504       TimeLeft += level.extra_time;
14505
14506       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14507
14508       DisplayGameControlValues();
14509     }
14510     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14511     {
14512       int shield_time = (element == EL_SHIELD_DEADLY ?
14513                          level.shield_deadly_time :
14514                          level.shield_normal_time);
14515
14516       player->shield_normal_time_left += shield_time;
14517       if (element == EL_SHIELD_DEADLY)
14518         player->shield_deadly_time_left += shield_time;
14519     }
14520     else if (element == EL_DYNAMITE ||
14521              element == EL_EM_DYNAMITE ||
14522              element == EL_SP_DISK_RED)
14523     {
14524       if (player->inventory_size < MAX_INVENTORY_SIZE)
14525         player->inventory_element[player->inventory_size++] = element;
14526
14527       DrawGameDoorValues();
14528     }
14529     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14530     {
14531       player->dynabomb_count++;
14532       player->dynabombs_left++;
14533     }
14534     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14535     {
14536       player->dynabomb_size++;
14537     }
14538     else if (element == EL_DYNABOMB_INCREASE_POWER)
14539     {
14540       player->dynabomb_xl = TRUE;
14541     }
14542     else if (IS_KEY(element))
14543     {
14544       player->key[KEY_NR(element)] = TRUE;
14545
14546       DrawGameDoorValues();
14547     }
14548     else if (element == EL_DC_KEY_WHITE)
14549     {
14550       player->num_white_keys++;
14551
14552       // display white keys?
14553       // DrawGameDoorValues();
14554     }
14555     else if (IS_ENVELOPE(element))
14556     {
14557       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14558
14559       if (!wait_for_snapping)
14560         player->show_envelope = element;
14561     }
14562     else if (element == EL_EMC_LENSES)
14563     {
14564       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14565
14566       RedrawAllInvisibleElementsForLenses();
14567     }
14568     else if (element == EL_EMC_MAGNIFIER)
14569     {
14570       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14571
14572       RedrawAllInvisibleElementsForMagnifier();
14573     }
14574     else if (IS_DROPPABLE(element) ||
14575              IS_THROWABLE(element))     // can be collected and dropped
14576     {
14577       int i;
14578
14579       if (collect_count == 0)
14580         player->inventory_infinite_element = element;
14581       else
14582         for (i = 0; i < collect_count; i++)
14583           if (player->inventory_size < MAX_INVENTORY_SIZE)
14584             player->inventory_element[player->inventory_size++] = element;
14585
14586       DrawGameDoorValues();
14587     }
14588     else if (collect_count > 0)
14589     {
14590       game.gems_still_needed -= collect_count;
14591       if (game.gems_still_needed < 0)
14592         game.gems_still_needed = 0;
14593
14594       game.snapshot.collected_item = TRUE;
14595
14596       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14597
14598       DisplayGameControlValues();
14599     }
14600
14601     RaiseScoreElement(element);
14602     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14603
14604     // use old behaviour for old levels (collecting)
14605     if (!level.finish_dig_collect && is_player)
14606     {
14607       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14608                                           player->index_bit, dig_side);
14609
14610       // if collecting triggered player relocation, finish collecting tile
14611       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14612         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14613     }
14614
14615     if (mode == DF_SNAP)
14616     {
14617       if (level.block_snap_field)
14618         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14619       else
14620         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14621
14622       // use old behaviour for old levels (snapping)
14623       if (!level.finish_dig_collect)
14624         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14625                                             player->index_bit, dig_side);
14626     }
14627   }
14628   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14629   {
14630     if (mode == DF_SNAP && element != EL_BD_ROCK)
14631       return MP_NO_ACTION;
14632
14633     if (CAN_FALL(element) && dy)
14634       return MP_NO_ACTION;
14635
14636     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14637         !(element == EL_SPRING && level.use_spring_bug))
14638       return MP_NO_ACTION;
14639
14640     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14641         ((move_direction & MV_VERTICAL &&
14642           ((element_info[element].move_pattern & MV_LEFT &&
14643             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14644            (element_info[element].move_pattern & MV_RIGHT &&
14645             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14646          (move_direction & MV_HORIZONTAL &&
14647           ((element_info[element].move_pattern & MV_UP &&
14648             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14649            (element_info[element].move_pattern & MV_DOWN &&
14650             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14651       return MP_NO_ACTION;
14652
14653     // do not push elements already moving away faster than player
14654     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14655         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14656       return MP_NO_ACTION;
14657
14658     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14659     {
14660       if (player->push_delay_value == -1 || !player_was_pushing)
14661         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14662     }
14663     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14664     {
14665       if (player->push_delay_value == -1)
14666         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14667     }
14668     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14669     {
14670       if (!player->is_pushing)
14671         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14672     }
14673
14674     player->is_pushing = TRUE;
14675     player->is_active = TRUE;
14676
14677     if (!(IN_LEV_FIELD(nextx, nexty) &&
14678           (IS_FREE(nextx, nexty) ||
14679            (IS_SB_ELEMENT(element) &&
14680             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14681            (IS_CUSTOM_ELEMENT(element) &&
14682             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14683       return MP_NO_ACTION;
14684
14685     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14686       return MP_NO_ACTION;
14687
14688     if (player->push_delay == -1)       // new pushing; restart delay
14689       player->push_delay = 0;
14690
14691     if (player->push_delay < player->push_delay_value &&
14692         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14693         element != EL_SPRING && element != EL_BALLOON)
14694     {
14695       // make sure that there is no move delay before next try to push
14696       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14697         player->move_delay = 0;
14698
14699       return MP_NO_ACTION;
14700     }
14701
14702     if (IS_CUSTOM_ELEMENT(element) &&
14703         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14704     {
14705       if (!DigFieldByCE(nextx, nexty, element))
14706         return MP_NO_ACTION;
14707     }
14708
14709     if (IS_SB_ELEMENT(element))
14710     {
14711       boolean sokoban_task_solved = FALSE;
14712
14713       if (element == EL_SOKOBAN_FIELD_FULL)
14714       {
14715         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14716
14717         IncrementSokobanFieldsNeeded();
14718         IncrementSokobanObjectsNeeded();
14719       }
14720
14721       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14722       {
14723         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14724
14725         DecrementSokobanFieldsNeeded();
14726         DecrementSokobanObjectsNeeded();
14727
14728         // sokoban object was pushed from empty field to sokoban field
14729         if (Back[x][y] == EL_EMPTY)
14730           sokoban_task_solved = TRUE;
14731       }
14732
14733       Tile[x][y] = EL_SOKOBAN_OBJECT;
14734
14735       if (Back[x][y] == Back[nextx][nexty])
14736         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14737       else if (Back[x][y] != 0)
14738         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14739                                     ACTION_EMPTYING);
14740       else
14741         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14742                                     ACTION_FILLING);
14743
14744       if (sokoban_task_solved &&
14745           game.sokoban_fields_still_needed == 0 &&
14746           game.sokoban_objects_still_needed == 0 &&
14747           level.auto_exit_sokoban)
14748       {
14749         game.players_still_needed = 0;
14750
14751         LevelSolved();
14752
14753         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14754       }
14755     }
14756     else
14757       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14758
14759     InitMovingField(x, y, move_direction);
14760     GfxAction[x][y] = ACTION_PUSHING;
14761
14762     if (mode == DF_SNAP)
14763       ContinueMoving(x, y);
14764     else
14765       MovPos[x][y] = (dx != 0 ? dx : dy);
14766
14767     Pushed[x][y] = TRUE;
14768     Pushed[nextx][nexty] = TRUE;
14769
14770     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14771       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14772     else
14773       player->push_delay_value = -1;    // get new value later
14774
14775     // check for element change _after_ element has been pushed
14776     if (game.use_change_when_pushing_bug)
14777     {
14778       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14779                                  player->index_bit, dig_side);
14780       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14781                                           player->index_bit, dig_side);
14782     }
14783   }
14784   else if (IS_SWITCHABLE(element))
14785   {
14786     if (PLAYER_SWITCHING(player, x, y))
14787     {
14788       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14789                                           player->index_bit, dig_side);
14790
14791       return MP_ACTION;
14792     }
14793
14794     player->is_switching = TRUE;
14795     player->switch_x = x;
14796     player->switch_y = y;
14797
14798     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14799
14800     if (element == EL_ROBOT_WHEEL)
14801     {
14802       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14803
14804       game.robot_wheel_x = x;
14805       game.robot_wheel_y = y;
14806       game.robot_wheel_active = TRUE;
14807
14808       TEST_DrawLevelField(x, y);
14809     }
14810     else if (element == EL_SP_TERMINAL)
14811     {
14812       int xx, yy;
14813
14814       SCAN_PLAYFIELD(xx, yy)
14815       {
14816         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14817         {
14818           Bang(xx, yy);
14819         }
14820         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14821         {
14822           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14823
14824           ResetGfxAnimation(xx, yy);
14825           TEST_DrawLevelField(xx, yy);
14826         }
14827       }
14828     }
14829     else if (IS_BELT_SWITCH(element))
14830     {
14831       ToggleBeltSwitch(x, y);
14832     }
14833     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14834              element == EL_SWITCHGATE_SWITCH_DOWN ||
14835              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14836              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14837     {
14838       ToggleSwitchgateSwitch();
14839     }
14840     else if (element == EL_LIGHT_SWITCH ||
14841              element == EL_LIGHT_SWITCH_ACTIVE)
14842     {
14843       ToggleLightSwitch(x, y);
14844     }
14845     else if (element == EL_TIMEGATE_SWITCH ||
14846              element == EL_DC_TIMEGATE_SWITCH)
14847     {
14848       ActivateTimegateSwitch(x, y);
14849     }
14850     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14851              element == EL_BALLOON_SWITCH_RIGHT ||
14852              element == EL_BALLOON_SWITCH_UP    ||
14853              element == EL_BALLOON_SWITCH_DOWN  ||
14854              element == EL_BALLOON_SWITCH_NONE  ||
14855              element == EL_BALLOON_SWITCH_ANY)
14856     {
14857       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14858                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14859                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14860                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14861                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14862                              move_direction);
14863     }
14864     else if (element == EL_LAMP)
14865     {
14866       Tile[x][y] = EL_LAMP_ACTIVE;
14867       game.lights_still_needed--;
14868
14869       ResetGfxAnimation(x, y);
14870       TEST_DrawLevelField(x, y);
14871     }
14872     else if (element == EL_TIME_ORB_FULL)
14873     {
14874       Tile[x][y] = EL_TIME_ORB_EMPTY;
14875
14876       if (level.time > 0 || level.use_time_orb_bug)
14877       {
14878         TimeLeft += level.time_orb_time;
14879         game.no_level_time_limit = FALSE;
14880
14881         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14882
14883         DisplayGameControlValues();
14884       }
14885
14886       ResetGfxAnimation(x, y);
14887       TEST_DrawLevelField(x, y);
14888     }
14889     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14890              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14891     {
14892       int xx, yy;
14893
14894       game.ball_active = !game.ball_active;
14895
14896       SCAN_PLAYFIELD(xx, yy)
14897       {
14898         int e = Tile[xx][yy];
14899
14900         if (game.ball_active)
14901         {
14902           if (e == EL_EMC_MAGIC_BALL)
14903             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14904           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14905             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14906         }
14907         else
14908         {
14909           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14910             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14911           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14912             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14913         }
14914       }
14915     }
14916
14917     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14918                                         player->index_bit, dig_side);
14919
14920     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14921                                         player->index_bit, dig_side);
14922
14923     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14924                                         player->index_bit, dig_side);
14925
14926     return MP_ACTION;
14927   }
14928   else
14929   {
14930     if (!PLAYER_SWITCHING(player, x, y))
14931     {
14932       player->is_switching = TRUE;
14933       player->switch_x = x;
14934       player->switch_y = y;
14935
14936       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14937                                  player->index_bit, dig_side);
14938       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14939                                           player->index_bit, dig_side);
14940
14941       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14942                                  player->index_bit, dig_side);
14943       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14944                                           player->index_bit, dig_side);
14945     }
14946
14947     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14948                                player->index_bit, dig_side);
14949     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14950                                         player->index_bit, dig_side);
14951
14952     return MP_NO_ACTION;
14953   }
14954
14955   player->push_delay = -1;
14956
14957   if (is_player)                // function can also be called by EL_PENGUIN
14958   {
14959     if (Tile[x][y] != element)          // really digged/collected something
14960     {
14961       player->is_collecting = !player->is_digging;
14962       player->is_active = TRUE;
14963
14964       player->last_removed_element = element;
14965     }
14966   }
14967
14968   return MP_MOVING;
14969 }
14970
14971 static boolean DigFieldByCE(int x, int y, int digging_element)
14972 {
14973   int element = Tile[x][y];
14974
14975   if (!IS_FREE(x, y))
14976   {
14977     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14978                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14979                   ACTION_BREAKING);
14980
14981     // no element can dig solid indestructible elements
14982     if (IS_INDESTRUCTIBLE(element) &&
14983         !IS_DIGGABLE(element) &&
14984         !IS_COLLECTIBLE(element))
14985       return FALSE;
14986
14987     if (AmoebaNr[x][y] &&
14988         (element == EL_AMOEBA_FULL ||
14989          element == EL_BD_AMOEBA ||
14990          element == EL_AMOEBA_GROWING))
14991     {
14992       AmoebaCnt[AmoebaNr[x][y]]--;
14993       AmoebaCnt2[AmoebaNr[x][y]]--;
14994     }
14995
14996     if (IS_MOVING(x, y))
14997       RemoveMovingField(x, y);
14998     else
14999     {
15000       RemoveField(x, y);
15001       TEST_DrawLevelField(x, y);
15002     }
15003
15004     // if digged element was about to explode, prevent the explosion
15005     ExplodeField[x][y] = EX_TYPE_NONE;
15006
15007     PlayLevelSoundAction(x, y, action);
15008   }
15009
15010   Store[x][y] = EL_EMPTY;
15011
15012   // this makes it possible to leave the removed element again
15013   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15014     Store[x][y] = element;
15015
15016   return TRUE;
15017 }
15018
15019 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15020 {
15021   int jx = player->jx, jy = player->jy;
15022   int x = jx + dx, y = jy + dy;
15023   int snap_direction = (dx == -1 ? MV_LEFT  :
15024                         dx == +1 ? MV_RIGHT :
15025                         dy == -1 ? MV_UP    :
15026                         dy == +1 ? MV_DOWN  : MV_NONE);
15027   boolean can_continue_snapping = (level.continuous_snapping &&
15028                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15029
15030   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15031     return FALSE;
15032
15033   if (!player->active || !IN_LEV_FIELD(x, y))
15034     return FALSE;
15035
15036   if (dx && dy)
15037     return FALSE;
15038
15039   if (!dx && !dy)
15040   {
15041     if (player->MovPos == 0)
15042       player->is_pushing = FALSE;
15043
15044     player->is_snapping = FALSE;
15045
15046     if (player->MovPos == 0)
15047     {
15048       player->is_moving = FALSE;
15049       player->is_digging = FALSE;
15050       player->is_collecting = FALSE;
15051     }
15052
15053     return FALSE;
15054   }
15055
15056   // prevent snapping with already pressed snap key when not allowed
15057   if (player->is_snapping && !can_continue_snapping)
15058     return FALSE;
15059
15060   player->MovDir = snap_direction;
15061
15062   if (player->MovPos == 0)
15063   {
15064     player->is_moving = FALSE;
15065     player->is_digging = FALSE;
15066     player->is_collecting = FALSE;
15067   }
15068
15069   player->is_dropping = FALSE;
15070   player->is_dropping_pressed = FALSE;
15071   player->drop_pressed_delay = 0;
15072
15073   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15074     return FALSE;
15075
15076   player->is_snapping = TRUE;
15077   player->is_active = TRUE;
15078
15079   if (player->MovPos == 0)
15080   {
15081     player->is_moving = FALSE;
15082     player->is_digging = FALSE;
15083     player->is_collecting = FALSE;
15084   }
15085
15086   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15087     TEST_DrawLevelField(player->last_jx, player->last_jy);
15088
15089   TEST_DrawLevelField(x, y);
15090
15091   return TRUE;
15092 }
15093
15094 static boolean DropElement(struct PlayerInfo *player)
15095 {
15096   int old_element, new_element;
15097   int dropx = player->jx, dropy = player->jy;
15098   int drop_direction = player->MovDir;
15099   int drop_side = drop_direction;
15100   int drop_element = get_next_dropped_element(player);
15101
15102   /* do not drop an element on top of another element; when holding drop key
15103      pressed without moving, dropped element must move away before the next
15104      element can be dropped (this is especially important if the next element
15105      is dynamite, which can be placed on background for historical reasons) */
15106   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15107     return MP_ACTION;
15108
15109   if (IS_THROWABLE(drop_element))
15110   {
15111     dropx += GET_DX_FROM_DIR(drop_direction);
15112     dropy += GET_DY_FROM_DIR(drop_direction);
15113
15114     if (!IN_LEV_FIELD(dropx, dropy))
15115       return FALSE;
15116   }
15117
15118   old_element = Tile[dropx][dropy];     // old element at dropping position
15119   new_element = drop_element;           // default: no change when dropping
15120
15121   // check if player is active, not moving and ready to drop
15122   if (!player->active || player->MovPos || player->drop_delay > 0)
15123     return FALSE;
15124
15125   // check if player has anything that can be dropped
15126   if (new_element == EL_UNDEFINED)
15127     return FALSE;
15128
15129   // only set if player has anything that can be dropped
15130   player->is_dropping_pressed = TRUE;
15131
15132   // check if drop key was pressed long enough for EM style dynamite
15133   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15134     return FALSE;
15135
15136   // check if anything can be dropped at the current position
15137   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15138     return FALSE;
15139
15140   // collected custom elements can only be dropped on empty fields
15141   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15142     return FALSE;
15143
15144   if (old_element != EL_EMPTY)
15145     Back[dropx][dropy] = old_element;   // store old element on this field
15146
15147   ResetGfxAnimation(dropx, dropy);
15148   ResetRandomAnimationValue(dropx, dropy);
15149
15150   if (player->inventory_size > 0 ||
15151       player->inventory_infinite_element != EL_UNDEFINED)
15152   {
15153     if (player->inventory_size > 0)
15154     {
15155       player->inventory_size--;
15156
15157       DrawGameDoorValues();
15158
15159       if (new_element == EL_DYNAMITE)
15160         new_element = EL_DYNAMITE_ACTIVE;
15161       else if (new_element == EL_EM_DYNAMITE)
15162         new_element = EL_EM_DYNAMITE_ACTIVE;
15163       else if (new_element == EL_SP_DISK_RED)
15164         new_element = EL_SP_DISK_RED_ACTIVE;
15165     }
15166
15167     Tile[dropx][dropy] = new_element;
15168
15169     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15170       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15171                           el2img(Tile[dropx][dropy]), 0);
15172
15173     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15174
15175     // needed if previous element just changed to "empty" in the last frame
15176     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15177
15178     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15179                                player->index_bit, drop_side);
15180     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15181                                         CE_PLAYER_DROPS_X,
15182                                         player->index_bit, drop_side);
15183
15184     TestIfElementTouchesCustomElement(dropx, dropy);
15185   }
15186   else          // player is dropping a dyna bomb
15187   {
15188     player->dynabombs_left--;
15189
15190     Tile[dropx][dropy] = new_element;
15191
15192     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15193       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15194                           el2img(Tile[dropx][dropy]), 0);
15195
15196     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15197   }
15198
15199   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15200     InitField_WithBug1(dropx, dropy, FALSE);
15201
15202   new_element = Tile[dropx][dropy];     // element might have changed
15203
15204   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15205       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15206   {
15207     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15208       MovDir[dropx][dropy] = drop_direction;
15209
15210     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15211
15212     // do not cause impact style collision by dropping elements that can fall
15213     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15214   }
15215
15216   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15217   player->is_dropping = TRUE;
15218
15219   player->drop_pressed_delay = 0;
15220   player->is_dropping_pressed = FALSE;
15221
15222   player->drop_x = dropx;
15223   player->drop_y = dropy;
15224
15225   return TRUE;
15226 }
15227
15228 // ----------------------------------------------------------------------------
15229 // game sound playing functions
15230 // ----------------------------------------------------------------------------
15231
15232 static int *loop_sound_frame = NULL;
15233 static int *loop_sound_volume = NULL;
15234
15235 void InitPlayLevelSound(void)
15236 {
15237   int num_sounds = getSoundListSize();
15238
15239   checked_free(loop_sound_frame);
15240   checked_free(loop_sound_volume);
15241
15242   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15243   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15244 }
15245
15246 static void PlayLevelSound(int x, int y, int nr)
15247 {
15248   int sx = SCREENX(x), sy = SCREENY(y);
15249   int volume, stereo_position;
15250   int max_distance = 8;
15251   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15252
15253   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15254       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15255     return;
15256
15257   if (!IN_LEV_FIELD(x, y) ||
15258       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15259       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15260     return;
15261
15262   volume = SOUND_MAX_VOLUME;
15263
15264   if (!IN_SCR_FIELD(sx, sy))
15265   {
15266     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15267     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15268
15269     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15270   }
15271
15272   stereo_position = (SOUND_MAX_LEFT +
15273                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15274                      (SCR_FIELDX + 2 * max_distance));
15275
15276   if (IS_LOOP_SOUND(nr))
15277   {
15278     /* This assures that quieter loop sounds do not overwrite louder ones,
15279        while restarting sound volume comparison with each new game frame. */
15280
15281     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15282       return;
15283
15284     loop_sound_volume[nr] = volume;
15285     loop_sound_frame[nr] = FrameCounter;
15286   }
15287
15288   PlaySoundExt(nr, volume, stereo_position, type);
15289 }
15290
15291 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15292 {
15293   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15294                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15295                  y < LEVELY(BY1) ? LEVELY(BY1) :
15296                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15297                  sound_action);
15298 }
15299
15300 static void PlayLevelSoundAction(int x, int y, int action)
15301 {
15302   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15303 }
15304
15305 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15306 {
15307   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15308
15309   if (sound_effect != SND_UNDEFINED)
15310     PlayLevelSound(x, y, sound_effect);
15311 }
15312
15313 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15314                                               int action)
15315 {
15316   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15317
15318   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15319     PlayLevelSound(x, y, sound_effect);
15320 }
15321
15322 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15323 {
15324   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15325
15326   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15327     PlayLevelSound(x, y, sound_effect);
15328 }
15329
15330 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15331 {
15332   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15333
15334   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15335     StopSound(sound_effect);
15336 }
15337
15338 static int getLevelMusicNr(void)
15339 {
15340   int level_pos = level_nr - leveldir_current->first_level;
15341
15342   if (levelset.music[level_nr] != MUS_UNDEFINED)
15343     return levelset.music[level_nr];            // from config file
15344   else
15345     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15346 }
15347
15348 static void FadeLevelSounds(void)
15349 {
15350   FadeSounds();
15351 }
15352
15353 static void FadeLevelMusic(void)
15354 {
15355   int music_nr = getLevelMusicNr();
15356   char *curr_music = getCurrentlyPlayingMusicFilename();
15357   char *next_music = getMusicInfoEntryFilename(music_nr);
15358
15359   if (!strEqual(curr_music, next_music))
15360     FadeMusic();
15361 }
15362
15363 void FadeLevelSoundsAndMusic(void)
15364 {
15365   FadeLevelSounds();
15366   FadeLevelMusic();
15367 }
15368
15369 static void PlayLevelMusic(void)
15370 {
15371   int music_nr = getLevelMusicNr();
15372   char *curr_music = getCurrentlyPlayingMusicFilename();
15373   char *next_music = getMusicInfoEntryFilename(music_nr);
15374
15375   if (!strEqual(curr_music, next_music))
15376     PlayMusicLoop(music_nr);
15377 }
15378
15379 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15380 {
15381   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15382   int offset = 0;
15383   int x = xx - offset;
15384   int y = yy - offset;
15385
15386   switch (sample)
15387   {
15388     case SOUND_blank:
15389       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15390       break;
15391
15392     case SOUND_roll:
15393       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15394       break;
15395
15396     case SOUND_stone:
15397       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15398       break;
15399
15400     case SOUND_nut:
15401       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15402       break;
15403
15404     case SOUND_crack:
15405       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15406       break;
15407
15408     case SOUND_bug:
15409       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15410       break;
15411
15412     case SOUND_tank:
15413       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15414       break;
15415
15416     case SOUND_android_clone:
15417       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15418       break;
15419
15420     case SOUND_android_move:
15421       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15422       break;
15423
15424     case SOUND_spring:
15425       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15426       break;
15427
15428     case SOUND_slurp:
15429       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15430       break;
15431
15432     case SOUND_eater:
15433       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15434       break;
15435
15436     case SOUND_eater_eat:
15437       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15438       break;
15439
15440     case SOUND_alien:
15441       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15442       break;
15443
15444     case SOUND_collect:
15445       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15446       break;
15447
15448     case SOUND_diamond:
15449       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15450       break;
15451
15452     case SOUND_squash:
15453       // !!! CHECK THIS !!!
15454 #if 1
15455       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15456 #else
15457       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15458 #endif
15459       break;
15460
15461     case SOUND_wonderfall:
15462       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15463       break;
15464
15465     case SOUND_drip:
15466       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15467       break;
15468
15469     case SOUND_push:
15470       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15471       break;
15472
15473     case SOUND_dirt:
15474       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15475       break;
15476
15477     case SOUND_acid:
15478       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15479       break;
15480
15481     case SOUND_ball:
15482       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15483       break;
15484
15485     case SOUND_slide:
15486       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15487       break;
15488
15489     case SOUND_wonder:
15490       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15491       break;
15492
15493     case SOUND_door:
15494       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15495       break;
15496
15497     case SOUND_exit_open:
15498       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15499       break;
15500
15501     case SOUND_exit_leave:
15502       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15503       break;
15504
15505     case SOUND_dynamite:
15506       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15507       break;
15508
15509     case SOUND_tick:
15510       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15511       break;
15512
15513     case SOUND_press:
15514       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15515       break;
15516
15517     case SOUND_wheel:
15518       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15519       break;
15520
15521     case SOUND_boom:
15522       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15523       break;
15524
15525     case SOUND_die:
15526       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15527       break;
15528
15529     case SOUND_time:
15530       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15531       break;
15532
15533     default:
15534       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15535       break;
15536   }
15537 }
15538
15539 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15540 {
15541   int element = map_element_SP_to_RND(element_sp);
15542   int action = map_action_SP_to_RND(action_sp);
15543   int offset = (setup.sp_show_border_elements ? 0 : 1);
15544   int x = xx - offset;
15545   int y = yy - offset;
15546
15547   PlayLevelSoundElementAction(x, y, element, action);
15548 }
15549
15550 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15551 {
15552   int element = map_element_MM_to_RND(element_mm);
15553   int action = map_action_MM_to_RND(action_mm);
15554   int offset = 0;
15555   int x = xx - offset;
15556   int y = yy - offset;
15557
15558   if (!IS_MM_ELEMENT(element))
15559     element = EL_MM_DEFAULT;
15560
15561   PlayLevelSoundElementAction(x, y, element, action);
15562 }
15563
15564 void PlaySound_MM(int sound_mm)
15565 {
15566   int sound = map_sound_MM_to_RND(sound_mm);
15567
15568   if (sound == SND_UNDEFINED)
15569     return;
15570
15571   PlaySound(sound);
15572 }
15573
15574 void PlaySoundLoop_MM(int sound_mm)
15575 {
15576   int sound = map_sound_MM_to_RND(sound_mm);
15577
15578   if (sound == SND_UNDEFINED)
15579     return;
15580
15581   PlaySoundLoop(sound);
15582 }
15583
15584 void StopSound_MM(int sound_mm)
15585 {
15586   int sound = map_sound_MM_to_RND(sound_mm);
15587
15588   if (sound == SND_UNDEFINED)
15589     return;
15590
15591   StopSound(sound);
15592 }
15593
15594 void RaiseScore(int value)
15595 {
15596   game.score += value;
15597
15598   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15599
15600   DisplayGameControlValues();
15601 }
15602
15603 void RaiseScoreElement(int element)
15604 {
15605   switch (element)
15606   {
15607     case EL_EMERALD:
15608     case EL_BD_DIAMOND:
15609     case EL_EMERALD_YELLOW:
15610     case EL_EMERALD_RED:
15611     case EL_EMERALD_PURPLE:
15612     case EL_SP_INFOTRON:
15613       RaiseScore(level.score[SC_EMERALD]);
15614       break;
15615     case EL_DIAMOND:
15616       RaiseScore(level.score[SC_DIAMOND]);
15617       break;
15618     case EL_CRYSTAL:
15619       RaiseScore(level.score[SC_CRYSTAL]);
15620       break;
15621     case EL_PEARL:
15622       RaiseScore(level.score[SC_PEARL]);
15623       break;
15624     case EL_BUG:
15625     case EL_BD_BUTTERFLY:
15626     case EL_SP_ELECTRON:
15627       RaiseScore(level.score[SC_BUG]);
15628       break;
15629     case EL_SPACESHIP:
15630     case EL_BD_FIREFLY:
15631     case EL_SP_SNIKSNAK:
15632       RaiseScore(level.score[SC_SPACESHIP]);
15633       break;
15634     case EL_YAMYAM:
15635     case EL_DARK_YAMYAM:
15636       RaiseScore(level.score[SC_YAMYAM]);
15637       break;
15638     case EL_ROBOT:
15639       RaiseScore(level.score[SC_ROBOT]);
15640       break;
15641     case EL_PACMAN:
15642       RaiseScore(level.score[SC_PACMAN]);
15643       break;
15644     case EL_NUT:
15645       RaiseScore(level.score[SC_NUT]);
15646       break;
15647     case EL_DYNAMITE:
15648     case EL_EM_DYNAMITE:
15649     case EL_SP_DISK_RED:
15650     case EL_DYNABOMB_INCREASE_NUMBER:
15651     case EL_DYNABOMB_INCREASE_SIZE:
15652     case EL_DYNABOMB_INCREASE_POWER:
15653       RaiseScore(level.score[SC_DYNAMITE]);
15654       break;
15655     case EL_SHIELD_NORMAL:
15656     case EL_SHIELD_DEADLY:
15657       RaiseScore(level.score[SC_SHIELD]);
15658       break;
15659     case EL_EXTRA_TIME:
15660       RaiseScore(level.extra_time_score);
15661       break;
15662     case EL_KEY_1:
15663     case EL_KEY_2:
15664     case EL_KEY_3:
15665     case EL_KEY_4:
15666     case EL_EM_KEY_1:
15667     case EL_EM_KEY_2:
15668     case EL_EM_KEY_3:
15669     case EL_EM_KEY_4:
15670     case EL_EMC_KEY_5:
15671     case EL_EMC_KEY_6:
15672     case EL_EMC_KEY_7:
15673     case EL_EMC_KEY_8:
15674     case EL_DC_KEY_WHITE:
15675       RaiseScore(level.score[SC_KEY]);
15676       break;
15677     default:
15678       RaiseScore(element_info[element].collect_score);
15679       break;
15680   }
15681 }
15682
15683 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15684 {
15685   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15686   {
15687     if (!quick_quit)
15688     {
15689       // prevent short reactivation of overlay buttons while closing door
15690       SetOverlayActive(FALSE);
15691       UnmapGameButtons();
15692
15693       // door may still be open due to skipped or envelope style request
15694       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15695     }
15696
15697     if (network.enabled)
15698       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15699     else
15700     {
15701       if (quick_quit)
15702         FadeSkipNextFadeIn();
15703
15704       SetGameStatus(GAME_MODE_MAIN);
15705
15706       DrawMainMenu();
15707     }
15708   }
15709   else          // continue playing the game
15710   {
15711     if (tape.playing && tape.deactivate_display)
15712       TapeDeactivateDisplayOff(TRUE);
15713
15714     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15715
15716     if (tape.playing && tape.deactivate_display)
15717       TapeDeactivateDisplayOn();
15718   }
15719 }
15720
15721 void RequestQuitGame(boolean escape_key_pressed)
15722 {
15723   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15724   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15725                         level_editor_test_game);
15726   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15727                           quick_quit || score_info_tape_play);
15728
15729   RequestQuitGameExt(skip_request, quick_quit,
15730                      "Do you really want to quit the game?");
15731 }
15732
15733 static char *getRestartGameMessage(void)
15734 {
15735   boolean play_again = hasStartedNetworkGame();
15736   static char message[MAX_OUTPUT_LINESIZE];
15737   char *game_over_text = "Game over!";
15738   char *play_again_text = " Play it again?";
15739
15740   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
15741       game_mm.game_over_message != NULL)
15742     game_over_text = game_mm.game_over_message;
15743
15744   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
15745            (play_again ? play_again_text : ""));
15746
15747   return message;
15748 }
15749
15750 static void RequestRestartGame(void)
15751 {
15752   char *message = getRestartGameMessage();
15753   boolean has_started_game = hasStartedNetworkGame();
15754   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15755   int door_state = DOOR_CLOSE_1;
15756
15757   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
15758   {
15759     CloseDoor(door_state);
15760
15761     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15762   }
15763   else
15764   {
15765     // if game was invoked from level editor, also close tape recorder door
15766     if (level_editor_test_game)
15767       door_state = DOOR_CLOSE_ALL;
15768
15769     CloseDoor(door_state);
15770
15771     SetGameStatus(GAME_MODE_MAIN);
15772
15773     DrawMainMenu();
15774   }
15775 }
15776
15777 boolean CheckRestartGame(void)
15778 {
15779   static int game_over_delay = 0;
15780   int game_over_delay_value = 50;
15781   boolean game_over = checkGameFailed();
15782
15783   if (!game_over)
15784   {
15785     game_over_delay = game_over_delay_value;
15786
15787     return FALSE;
15788   }
15789
15790   if (game_over_delay > 0)
15791   {
15792     if (game_over_delay == game_over_delay_value / 2)
15793       PlaySound(SND_GAME_LOSING);
15794
15795     game_over_delay--;
15796
15797     return FALSE;
15798   }
15799
15800   // do not handle game over if request dialog is already active
15801   if (game.request_active)
15802     return FALSE;
15803
15804   // do not ask to play again if game was never actually played
15805   if (!game.GamePlayed)
15806     return FALSE;
15807
15808   // do not ask to play again if this was disabled in setup menu
15809   if (!setup.ask_on_game_over)
15810     return FALSE;
15811
15812   RequestRestartGame();
15813
15814   return TRUE;
15815 }
15816
15817 boolean checkGameSolved(void)
15818 {
15819   // set for all game engines if level was solved
15820   return game.LevelSolved_GameEnd;
15821 }
15822
15823 boolean checkGameFailed(void)
15824 {
15825   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15826     return (game_em.game_over && !game_em.level_solved);
15827   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15828     return (game_sp.game_over && !game_sp.level_solved);
15829   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15830     return (game_mm.game_over && !game_mm.level_solved);
15831   else                          // GAME_ENGINE_TYPE_RND
15832     return (game.GameOver && !game.LevelSolved);
15833 }
15834
15835 boolean checkGameEnded(void)
15836 {
15837   return (checkGameSolved() || checkGameFailed());
15838 }
15839
15840
15841 // ----------------------------------------------------------------------------
15842 // random generator functions
15843 // ----------------------------------------------------------------------------
15844
15845 unsigned int InitEngineRandom_RND(int seed)
15846 {
15847   game.num_random_calls = 0;
15848
15849   return InitEngineRandom(seed);
15850 }
15851
15852 unsigned int RND(int max)
15853 {
15854   if (max > 0)
15855   {
15856     game.num_random_calls++;
15857
15858     return GetEngineRandom(max);
15859   }
15860
15861   return 0;
15862 }
15863
15864
15865 // ----------------------------------------------------------------------------
15866 // game engine snapshot handling functions
15867 // ----------------------------------------------------------------------------
15868
15869 struct EngineSnapshotInfo
15870 {
15871   // runtime values for custom element collect score
15872   int collect_score[NUM_CUSTOM_ELEMENTS];
15873
15874   // runtime values for group element choice position
15875   int choice_pos[NUM_GROUP_ELEMENTS];
15876
15877   // runtime values for belt position animations
15878   int belt_graphic[4][NUM_BELT_PARTS];
15879   int belt_anim_mode[4][NUM_BELT_PARTS];
15880 };
15881
15882 static struct EngineSnapshotInfo engine_snapshot_rnd;
15883 static char *snapshot_level_identifier = NULL;
15884 static int snapshot_level_nr = -1;
15885
15886 static void SaveEngineSnapshotValues_RND(void)
15887 {
15888   static int belt_base_active_element[4] =
15889   {
15890     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15891     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15892     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15893     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15894   };
15895   int i, j;
15896
15897   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15898   {
15899     int element = EL_CUSTOM_START + i;
15900
15901     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15902   }
15903
15904   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15905   {
15906     int element = EL_GROUP_START + i;
15907
15908     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15909   }
15910
15911   for (i = 0; i < 4; i++)
15912   {
15913     for (j = 0; j < NUM_BELT_PARTS; j++)
15914     {
15915       int element = belt_base_active_element[i] + j;
15916       int graphic = el2img(element);
15917       int anim_mode = graphic_info[graphic].anim_mode;
15918
15919       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15920       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15921     }
15922   }
15923 }
15924
15925 static void LoadEngineSnapshotValues_RND(void)
15926 {
15927   unsigned int num_random_calls = game.num_random_calls;
15928   int i, j;
15929
15930   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15931   {
15932     int element = EL_CUSTOM_START + i;
15933
15934     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15935   }
15936
15937   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15938   {
15939     int element = EL_GROUP_START + i;
15940
15941     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15942   }
15943
15944   for (i = 0; i < 4; i++)
15945   {
15946     for (j = 0; j < NUM_BELT_PARTS; j++)
15947     {
15948       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15949       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15950
15951       graphic_info[graphic].anim_mode = anim_mode;
15952     }
15953   }
15954
15955   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15956   {
15957     InitRND(tape.random_seed);
15958     for (i = 0; i < num_random_calls; i++)
15959       RND(1);
15960   }
15961
15962   if (game.num_random_calls != num_random_calls)
15963   {
15964     Error("number of random calls out of sync");
15965     Error("number of random calls should be %d", num_random_calls);
15966     Error("number of random calls is %d", game.num_random_calls);
15967
15968     Fail("this should not happen -- please debug");
15969   }
15970 }
15971
15972 void FreeEngineSnapshotSingle(void)
15973 {
15974   FreeSnapshotSingle();
15975
15976   setString(&snapshot_level_identifier, NULL);
15977   snapshot_level_nr = -1;
15978 }
15979
15980 void FreeEngineSnapshotList(void)
15981 {
15982   FreeSnapshotList();
15983 }
15984
15985 static ListNode *SaveEngineSnapshotBuffers(void)
15986 {
15987   ListNode *buffers = NULL;
15988
15989   // copy some special values to a structure better suited for the snapshot
15990
15991   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15992     SaveEngineSnapshotValues_RND();
15993   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15994     SaveEngineSnapshotValues_EM();
15995   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15996     SaveEngineSnapshotValues_SP(&buffers);
15997   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15998     SaveEngineSnapshotValues_MM();
15999
16000   // save values stored in special snapshot structure
16001
16002   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16003     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16004   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16005     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16006   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16007     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16008   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16009     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
16010
16011   // save further RND engine values
16012
16013   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
16014   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
16015   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
16016
16017   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16018   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16019   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16020   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16021   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16022
16023   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16024   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16025   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16026
16027   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16028
16029   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16030   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16031
16032   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
16033   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
16034   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
16035   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16036   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16037   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16038   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16039   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16040   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16041   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16042   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16043   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16044   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16045   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16046   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16047   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16048   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16049   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16050
16051   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16052   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16053
16054   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16055   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16056   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16057
16058   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16059   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16060
16061   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16062   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16063   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16064   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16065   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16066   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16067
16068   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16069   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16070
16071 #if 0
16072   ListNode *node = engine_snapshot_list_rnd;
16073   int num_bytes = 0;
16074
16075   while (node != NULL)
16076   {
16077     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16078
16079     node = node->next;
16080   }
16081
16082   Debug("game:playing:SaveEngineSnapshotBuffers",
16083         "size of engine snapshot: %d bytes", num_bytes);
16084 #endif
16085
16086   return buffers;
16087 }
16088
16089 void SaveEngineSnapshotSingle(void)
16090 {
16091   ListNode *buffers = SaveEngineSnapshotBuffers();
16092
16093   // finally save all snapshot buffers to single snapshot
16094   SaveSnapshotSingle(buffers);
16095
16096   // save level identification information
16097   setString(&snapshot_level_identifier, leveldir_current->identifier);
16098   snapshot_level_nr = level_nr;
16099 }
16100
16101 boolean CheckSaveEngineSnapshotToList(void)
16102 {
16103   boolean save_snapshot =
16104     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16105      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16106       game.snapshot.changed_action) ||
16107      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16108       game.snapshot.collected_item));
16109
16110   game.snapshot.changed_action = FALSE;
16111   game.snapshot.collected_item = FALSE;
16112   game.snapshot.save_snapshot = save_snapshot;
16113
16114   return save_snapshot;
16115 }
16116
16117 void SaveEngineSnapshotToList(void)
16118 {
16119   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16120       tape.quick_resume)
16121     return;
16122
16123   ListNode *buffers = SaveEngineSnapshotBuffers();
16124
16125   // finally save all snapshot buffers to snapshot list
16126   SaveSnapshotToList(buffers);
16127 }
16128
16129 void SaveEngineSnapshotToListInitial(void)
16130 {
16131   FreeEngineSnapshotList();
16132
16133   SaveEngineSnapshotToList();
16134 }
16135
16136 static void LoadEngineSnapshotValues(void)
16137 {
16138   // restore special values from snapshot structure
16139
16140   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16141     LoadEngineSnapshotValues_RND();
16142   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16143     LoadEngineSnapshotValues_EM();
16144   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16145     LoadEngineSnapshotValues_SP();
16146   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16147     LoadEngineSnapshotValues_MM();
16148 }
16149
16150 void LoadEngineSnapshotSingle(void)
16151 {
16152   LoadSnapshotSingle();
16153
16154   LoadEngineSnapshotValues();
16155 }
16156
16157 static void LoadEngineSnapshot_Undo(int steps)
16158 {
16159   LoadSnapshotFromList_Older(steps);
16160
16161   LoadEngineSnapshotValues();
16162 }
16163
16164 static void LoadEngineSnapshot_Redo(int steps)
16165 {
16166   LoadSnapshotFromList_Newer(steps);
16167
16168   LoadEngineSnapshotValues();
16169 }
16170
16171 boolean CheckEngineSnapshotSingle(void)
16172 {
16173   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16174           snapshot_level_nr == level_nr);
16175 }
16176
16177 boolean CheckEngineSnapshotList(void)
16178 {
16179   return CheckSnapshotList();
16180 }
16181
16182
16183 // ---------- new game button stuff -------------------------------------------
16184
16185 static struct
16186 {
16187   int graphic;
16188   struct XY *pos;
16189   int gadget_id;
16190   boolean *setup_value;
16191   boolean allowed_on_tape;
16192   boolean is_touch_button;
16193   char *infotext;
16194 } gamebutton_info[NUM_GAME_BUTTONS] =
16195 {
16196   {
16197     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16198     GAME_CTRL_ID_STOP,                          NULL,
16199     TRUE, FALSE,                                "stop game"
16200   },
16201   {
16202     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16203     GAME_CTRL_ID_PAUSE,                         NULL,
16204     TRUE, FALSE,                                "pause game"
16205   },
16206   {
16207     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16208     GAME_CTRL_ID_PLAY,                          NULL,
16209     TRUE, FALSE,                                "play game"
16210   },
16211   {
16212     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16213     GAME_CTRL_ID_UNDO,                          NULL,
16214     TRUE, FALSE,                                "undo step"
16215   },
16216   {
16217     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16218     GAME_CTRL_ID_REDO,                          NULL,
16219     TRUE, FALSE,                                "redo step"
16220   },
16221   {
16222     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16223     GAME_CTRL_ID_SAVE,                          NULL,
16224     TRUE, FALSE,                                "save game"
16225   },
16226   {
16227     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16228     GAME_CTRL_ID_PAUSE2,                        NULL,
16229     TRUE, FALSE,                                "pause game"
16230   },
16231   {
16232     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16233     GAME_CTRL_ID_LOAD,                          NULL,
16234     TRUE, FALSE,                                "load game"
16235   },
16236   {
16237     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16238     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16239     FALSE, FALSE,                               "stop game"
16240   },
16241   {
16242     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16243     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16244     FALSE, FALSE,                               "pause game"
16245   },
16246   {
16247     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16248     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16249     FALSE, FALSE,                               "play game"
16250   },
16251   {
16252     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16253     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16254     FALSE, TRUE,                                "stop game"
16255   },
16256   {
16257     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16258     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16259     FALSE, TRUE,                                "pause game"
16260   },
16261   {
16262     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16263     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16264     TRUE, FALSE,                                "background music on/off"
16265   },
16266   {
16267     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16268     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16269     TRUE, FALSE,                                "sound loops on/off"
16270   },
16271   {
16272     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16273     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16274     TRUE, FALSE,                                "normal sounds on/off"
16275   },
16276   {
16277     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16278     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16279     FALSE, FALSE,                               "background music on/off"
16280   },
16281   {
16282     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16283     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16284     FALSE, FALSE,                               "sound loops on/off"
16285   },
16286   {
16287     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16288     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16289     FALSE, FALSE,                               "normal sounds on/off"
16290   }
16291 };
16292
16293 void CreateGameButtons(void)
16294 {
16295   int i;
16296
16297   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16298   {
16299     int graphic = gamebutton_info[i].graphic;
16300     struct GraphicInfo *gfx = &graphic_info[graphic];
16301     struct XY *pos = gamebutton_info[i].pos;
16302     struct GadgetInfo *gi;
16303     int button_type;
16304     boolean checked;
16305     unsigned int event_mask;
16306     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16307     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16308     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16309     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16310     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16311     int gd_x   = gfx->src_x;
16312     int gd_y   = gfx->src_y;
16313     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16314     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16315     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16316     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16317     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16318     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16319     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16320     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16321     int id = i;
16322
16323     // do not use touch buttons if overlay touch buttons are disabled
16324     if (is_touch_button && !setup.touch.overlay_buttons)
16325       continue;
16326
16327     if (gfx->bitmap == NULL)
16328     {
16329       game_gadget[id] = NULL;
16330
16331       continue;
16332     }
16333
16334     if (id == GAME_CTRL_ID_STOP ||
16335         id == GAME_CTRL_ID_PANEL_STOP ||
16336         id == GAME_CTRL_ID_TOUCH_STOP ||
16337         id == GAME_CTRL_ID_PLAY ||
16338         id == GAME_CTRL_ID_PANEL_PLAY ||
16339         id == GAME_CTRL_ID_SAVE ||
16340         id == GAME_CTRL_ID_LOAD)
16341     {
16342       button_type = GD_TYPE_NORMAL_BUTTON;
16343       checked = FALSE;
16344       event_mask = GD_EVENT_RELEASED;
16345     }
16346     else if (id == GAME_CTRL_ID_UNDO ||
16347              id == GAME_CTRL_ID_REDO)
16348     {
16349       button_type = GD_TYPE_NORMAL_BUTTON;
16350       checked = FALSE;
16351       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16352     }
16353     else
16354     {
16355       button_type = GD_TYPE_CHECK_BUTTON;
16356       checked = (gamebutton_info[i].setup_value != NULL ?
16357                  *gamebutton_info[i].setup_value : FALSE);
16358       event_mask = GD_EVENT_PRESSED;
16359     }
16360
16361     gi = CreateGadget(GDI_CUSTOM_ID, id,
16362                       GDI_IMAGE_ID, graphic,
16363                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16364                       GDI_X, base_x + x,
16365                       GDI_Y, base_y + y,
16366                       GDI_WIDTH, gfx->width,
16367                       GDI_HEIGHT, gfx->height,
16368                       GDI_TYPE, button_type,
16369                       GDI_STATE, GD_BUTTON_UNPRESSED,
16370                       GDI_CHECKED, checked,
16371                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16372                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16373                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16374                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16375                       GDI_DIRECT_DRAW, FALSE,
16376                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16377                       GDI_EVENT_MASK, event_mask,
16378                       GDI_CALLBACK_ACTION, HandleGameButtons,
16379                       GDI_END);
16380
16381     if (gi == NULL)
16382       Fail("cannot create gadget");
16383
16384     game_gadget[id] = gi;
16385   }
16386 }
16387
16388 void FreeGameButtons(void)
16389 {
16390   int i;
16391
16392   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16393     FreeGadget(game_gadget[i]);
16394 }
16395
16396 static void UnmapGameButtonsAtSamePosition(int id)
16397 {
16398   int i;
16399
16400   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16401     if (i != id &&
16402         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16403         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16404       UnmapGadget(game_gadget[i]);
16405 }
16406
16407 static void UnmapGameButtonsAtSamePosition_All(void)
16408 {
16409   if (setup.show_load_save_buttons)
16410   {
16411     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16412     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16413     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16414   }
16415   else if (setup.show_undo_redo_buttons)
16416   {
16417     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16418     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16419     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16420   }
16421   else
16422   {
16423     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16424     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16425     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16426
16427     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16428     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16429     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16430   }
16431 }
16432
16433 void MapLoadSaveButtons(void)
16434 {
16435   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16436   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16437
16438   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16439   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16440 }
16441
16442 void MapUndoRedoButtons(void)
16443 {
16444   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16445   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16446
16447   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16448   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16449 }
16450
16451 void ModifyPauseButtons(void)
16452 {
16453   static int ids[] =
16454   {
16455     GAME_CTRL_ID_PAUSE,
16456     GAME_CTRL_ID_PAUSE2,
16457     GAME_CTRL_ID_PANEL_PAUSE,
16458     GAME_CTRL_ID_TOUCH_PAUSE,
16459     -1
16460   };
16461   int i;
16462
16463   for (i = 0; ids[i] > -1; i++)
16464     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16465 }
16466
16467 static void MapGameButtonsExt(boolean on_tape)
16468 {
16469   int i;
16470
16471   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16472   {
16473     if ((i == GAME_CTRL_ID_UNDO ||
16474          i == GAME_CTRL_ID_REDO) &&
16475         game_status != GAME_MODE_PLAYING)
16476       continue;
16477
16478     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16479       MapGadget(game_gadget[i]);
16480   }
16481
16482   UnmapGameButtonsAtSamePosition_All();
16483
16484   RedrawGameButtons();
16485 }
16486
16487 static void UnmapGameButtonsExt(boolean on_tape)
16488 {
16489   int i;
16490
16491   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16492     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16493       UnmapGadget(game_gadget[i]);
16494 }
16495
16496 static void RedrawGameButtonsExt(boolean on_tape)
16497 {
16498   int i;
16499
16500   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16501     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16502       RedrawGadget(game_gadget[i]);
16503 }
16504
16505 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16506 {
16507   if (gi == NULL)
16508     return;
16509
16510   gi->checked = state;
16511 }
16512
16513 static void RedrawSoundButtonGadget(int id)
16514 {
16515   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16516              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16517              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16518              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16519              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16520              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16521              id);
16522
16523   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16524   RedrawGadget(game_gadget[id2]);
16525 }
16526
16527 void MapGameButtons(void)
16528 {
16529   MapGameButtonsExt(FALSE);
16530 }
16531
16532 void UnmapGameButtons(void)
16533 {
16534   UnmapGameButtonsExt(FALSE);
16535 }
16536
16537 void RedrawGameButtons(void)
16538 {
16539   RedrawGameButtonsExt(FALSE);
16540 }
16541
16542 void MapGameButtonsOnTape(void)
16543 {
16544   MapGameButtonsExt(TRUE);
16545 }
16546
16547 void UnmapGameButtonsOnTape(void)
16548 {
16549   UnmapGameButtonsExt(TRUE);
16550 }
16551
16552 void RedrawGameButtonsOnTape(void)
16553 {
16554   RedrawGameButtonsExt(TRUE);
16555 }
16556
16557 static void GameUndoRedoExt(void)
16558 {
16559   ClearPlayerAction();
16560
16561   tape.pausing = TRUE;
16562
16563   RedrawPlayfield();
16564   UpdateAndDisplayGameControlValues();
16565
16566   DrawCompleteVideoDisplay();
16567   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16568   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16569   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16570
16571   ModifyPauseButtons();
16572
16573   BackToFront();
16574 }
16575
16576 static void GameUndo(int steps)
16577 {
16578   if (!CheckEngineSnapshotList())
16579     return;
16580
16581   int tape_property_bits = tape.property_bits;
16582
16583   LoadEngineSnapshot_Undo(steps);
16584
16585   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16586
16587   GameUndoRedoExt();
16588 }
16589
16590 static void GameRedo(int steps)
16591 {
16592   if (!CheckEngineSnapshotList())
16593     return;
16594
16595   int tape_property_bits = tape.property_bits;
16596
16597   LoadEngineSnapshot_Redo(steps);
16598
16599   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16600
16601   GameUndoRedoExt();
16602 }
16603
16604 static void HandleGameButtonsExt(int id, int button)
16605 {
16606   static boolean game_undo_executed = FALSE;
16607   int steps = BUTTON_STEPSIZE(button);
16608   boolean handle_game_buttons =
16609     (game_status == GAME_MODE_PLAYING ||
16610      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16611
16612   if (!handle_game_buttons)
16613     return;
16614
16615   switch (id)
16616   {
16617     case GAME_CTRL_ID_STOP:
16618     case GAME_CTRL_ID_PANEL_STOP:
16619     case GAME_CTRL_ID_TOUCH_STOP:
16620       TapeStopGame();
16621
16622       break;
16623
16624     case GAME_CTRL_ID_PAUSE:
16625     case GAME_CTRL_ID_PAUSE2:
16626     case GAME_CTRL_ID_PANEL_PAUSE:
16627     case GAME_CTRL_ID_TOUCH_PAUSE:
16628       if (network.enabled && game_status == GAME_MODE_PLAYING)
16629       {
16630         if (tape.pausing)
16631           SendToServer_ContinuePlaying();
16632         else
16633           SendToServer_PausePlaying();
16634       }
16635       else
16636         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16637
16638       game_undo_executed = FALSE;
16639
16640       break;
16641
16642     case GAME_CTRL_ID_PLAY:
16643     case GAME_CTRL_ID_PANEL_PLAY:
16644       if (game_status == GAME_MODE_MAIN)
16645       {
16646         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16647       }
16648       else if (tape.pausing)
16649       {
16650         if (network.enabled)
16651           SendToServer_ContinuePlaying();
16652         else
16653           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16654       }
16655       break;
16656
16657     case GAME_CTRL_ID_UNDO:
16658       // Important: When using "save snapshot when collecting an item" mode,
16659       // load last (current) snapshot for first "undo" after pressing "pause"
16660       // (else the last-but-one snapshot would be loaded, because the snapshot
16661       // pointer already points to the last snapshot when pressing "pause",
16662       // which is fine for "every step/move" mode, but not for "every collect")
16663       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16664           !game_undo_executed)
16665         steps--;
16666
16667       game_undo_executed = TRUE;
16668
16669       GameUndo(steps);
16670       break;
16671
16672     case GAME_CTRL_ID_REDO:
16673       GameRedo(steps);
16674       break;
16675
16676     case GAME_CTRL_ID_SAVE:
16677       TapeQuickSave();
16678       break;
16679
16680     case GAME_CTRL_ID_LOAD:
16681       TapeQuickLoad();
16682       break;
16683
16684     case SOUND_CTRL_ID_MUSIC:
16685     case SOUND_CTRL_ID_PANEL_MUSIC:
16686       if (setup.sound_music)
16687       { 
16688         setup.sound_music = FALSE;
16689
16690         FadeMusic();
16691       }
16692       else if (audio.music_available)
16693       { 
16694         setup.sound = setup.sound_music = TRUE;
16695
16696         SetAudioMode(setup.sound);
16697
16698         if (game_status == GAME_MODE_PLAYING)
16699           PlayLevelMusic();
16700       }
16701
16702       RedrawSoundButtonGadget(id);
16703
16704       break;
16705
16706     case SOUND_CTRL_ID_LOOPS:
16707     case SOUND_CTRL_ID_PANEL_LOOPS:
16708       if (setup.sound_loops)
16709         setup.sound_loops = FALSE;
16710       else if (audio.loops_available)
16711       {
16712         setup.sound = setup.sound_loops = TRUE;
16713
16714         SetAudioMode(setup.sound);
16715       }
16716
16717       RedrawSoundButtonGadget(id);
16718
16719       break;
16720
16721     case SOUND_CTRL_ID_SIMPLE:
16722     case SOUND_CTRL_ID_PANEL_SIMPLE:
16723       if (setup.sound_simple)
16724         setup.sound_simple = FALSE;
16725       else if (audio.sound_available)
16726       {
16727         setup.sound = setup.sound_simple = TRUE;
16728
16729         SetAudioMode(setup.sound);
16730       }
16731
16732       RedrawSoundButtonGadget(id);
16733
16734       break;
16735
16736     default:
16737       break;
16738   }
16739 }
16740
16741 static void HandleGameButtons(struct GadgetInfo *gi)
16742 {
16743   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16744 }
16745
16746 void HandleSoundButtonKeys(Key key)
16747 {
16748   if (key == setup.shortcut.sound_simple)
16749     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16750   else if (key == setup.shortcut.sound_loops)
16751     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16752   else if (key == setup.shortcut.sound_music)
16753     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16754 }