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