added basic support for additional empty space elements in game engine
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1558
1559 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1560 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1561 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1562                                  IS_JUST_CHANGING(x, y))
1563
1564 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1565
1566 // static variables for playfield scan mode (scanning forward or backward)
1567 static int playfield_scan_start_x = 0;
1568 static int playfield_scan_start_y = 0;
1569 static int playfield_scan_delta_x = 1;
1570 static int playfield_scan_delta_y = 1;
1571
1572 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1573                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1574                                      (y) += playfield_scan_delta_y)     \
1575                                 for ((x) = playfield_scan_start_x;      \
1576                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1577                                      (x) += playfield_scan_delta_x)
1578
1579 #ifdef DEBUG
1580 void DEBUG_SetMaximumDynamite(void)
1581 {
1582   int i;
1583
1584   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1585     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1586       local_player->inventory_element[local_player->inventory_size++] =
1587         EL_DYNAMITE;
1588 }
1589 #endif
1590
1591 static void InitPlayfieldScanModeVars(void)
1592 {
1593   if (game.use_reverse_scan_direction)
1594   {
1595     playfield_scan_start_x = lev_fieldx - 1;
1596     playfield_scan_start_y = lev_fieldy - 1;
1597
1598     playfield_scan_delta_x = -1;
1599     playfield_scan_delta_y = -1;
1600   }
1601   else
1602   {
1603     playfield_scan_start_x = 0;
1604     playfield_scan_start_y = 0;
1605
1606     playfield_scan_delta_x = 1;
1607     playfield_scan_delta_y = 1;
1608   }
1609 }
1610
1611 static void InitPlayfieldScanMode(int mode)
1612 {
1613   game.use_reverse_scan_direction =
1614     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1615
1616   InitPlayfieldScanModeVars();
1617 }
1618
1619 static int get_move_delay_from_stepsize(int move_stepsize)
1620 {
1621   move_stepsize =
1622     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1623
1624   // make sure that stepsize value is always a power of 2
1625   move_stepsize = (1 << log_2(move_stepsize));
1626
1627   return TILEX / move_stepsize;
1628 }
1629
1630 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1631                                boolean init_game)
1632 {
1633   int player_nr = player->index_nr;
1634   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1635   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1636
1637   // do no immediately change move delay -- the player might just be moving
1638   player->move_delay_value_next = move_delay;
1639
1640   // information if player can move must be set separately
1641   player->cannot_move = cannot_move;
1642
1643   if (init_game)
1644   {
1645     player->move_delay       = game.initial_move_delay[player_nr];
1646     player->move_delay_value = game.initial_move_delay_value[player_nr];
1647
1648     player->move_delay_value_next = -1;
1649
1650     player->move_delay_reset_counter = 0;
1651   }
1652 }
1653
1654 void GetPlayerConfig(void)
1655 {
1656   GameFrameDelay = setup.game_frame_delay;
1657
1658   if (!audio.sound_available)
1659     setup.sound_simple = FALSE;
1660
1661   if (!audio.loops_available)
1662     setup.sound_loops = FALSE;
1663
1664   if (!audio.music_available)
1665     setup.sound_music = FALSE;
1666
1667   if (!video.fullscreen_available)
1668     setup.fullscreen = FALSE;
1669
1670   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1671
1672   SetAudioMode(setup.sound);
1673 }
1674
1675 int GetElementFromGroupElement(int element)
1676 {
1677   if (IS_GROUP_ELEMENT(element))
1678   {
1679     struct ElementGroupInfo *group = element_info[element].group;
1680     int last_anim_random_frame = gfx.anim_random_frame;
1681     int element_pos;
1682
1683     if (group->choice_mode == ANIM_RANDOM)
1684       gfx.anim_random_frame = RND(group->num_elements_resolved);
1685
1686     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1687                                     group->choice_mode, 0,
1688                                     group->choice_pos);
1689
1690     if (group->choice_mode == ANIM_RANDOM)
1691       gfx.anim_random_frame = last_anim_random_frame;
1692
1693     group->choice_pos++;
1694
1695     element = group->element_resolved[element_pos];
1696   }
1697
1698   return element;
1699 }
1700
1701 static void IncrementSokobanFieldsNeeded(void)
1702 {
1703   if (level.sb_fields_needed)
1704     game.sokoban_fields_still_needed++;
1705 }
1706
1707 static void IncrementSokobanObjectsNeeded(void)
1708 {
1709   if (level.sb_objects_needed)
1710     game.sokoban_objects_still_needed++;
1711 }
1712
1713 static void DecrementSokobanFieldsNeeded(void)
1714 {
1715   if (game.sokoban_fields_still_needed > 0)
1716     game.sokoban_fields_still_needed--;
1717 }
1718
1719 static void DecrementSokobanObjectsNeeded(void)
1720 {
1721   if (game.sokoban_objects_still_needed > 0)
1722     game.sokoban_objects_still_needed--;
1723 }
1724
1725 static void InitPlayerField(int x, int y, int element, boolean init_game)
1726 {
1727   if (element == EL_SP_MURPHY)
1728   {
1729     if (init_game)
1730     {
1731       if (stored_player[0].present)
1732       {
1733         Tile[x][y] = EL_SP_MURPHY_CLONE;
1734
1735         return;
1736       }
1737       else
1738       {
1739         stored_player[0].initial_element = element;
1740         stored_player[0].use_murphy = TRUE;
1741
1742         if (!level.use_artwork_element[0])
1743           stored_player[0].artwork_element = EL_SP_MURPHY;
1744       }
1745
1746       Tile[x][y] = EL_PLAYER_1;
1747     }
1748   }
1749
1750   if (init_game)
1751   {
1752     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1753     int jx = player->jx, jy = player->jy;
1754
1755     player->present = TRUE;
1756
1757     player->block_last_field = (element == EL_SP_MURPHY ?
1758                                 level.sp_block_last_field :
1759                                 level.block_last_field);
1760
1761     // ---------- initialize player's last field block delay ------------------
1762
1763     // always start with reliable default value (no adjustment needed)
1764     player->block_delay_adjustment = 0;
1765
1766     // special case 1: in Supaplex, Murphy blocks last field one more frame
1767     if (player->block_last_field && element == EL_SP_MURPHY)
1768       player->block_delay_adjustment = 1;
1769
1770     // special case 2: in game engines before 3.1.1, blocking was different
1771     if (game.use_block_last_field_bug)
1772       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1773
1774     if (!network.enabled || player->connected_network)
1775     {
1776       player->active = TRUE;
1777
1778       // remove potentially duplicate players
1779       if (StorePlayer[jx][jy] == Tile[x][y])
1780         StorePlayer[jx][jy] = 0;
1781
1782       StorePlayer[x][y] = Tile[x][y];
1783
1784 #if DEBUG_INIT_PLAYER
1785       Debug("game:init:player", "- player element %d activated",
1786             player->element_nr);
1787       Debug("game:init:player", "  (local player is %d and currently %s)",
1788             local_player->element_nr,
1789             local_player->active ? "active" : "not active");
1790     }
1791 #endif
1792
1793     Tile[x][y] = EL_EMPTY;
1794
1795     player->jx = player->last_jx = x;
1796     player->jy = player->last_jy = y;
1797   }
1798
1799   // always check if player was just killed and should be reanimated
1800   {
1801     int player_nr = GET_PLAYER_NR(element);
1802     struct PlayerInfo *player = &stored_player[player_nr];
1803
1804     if (player->active && player->killed)
1805       player->reanimated = TRUE; // if player was just killed, reanimate him
1806   }
1807 }
1808
1809 static void InitField(int x, int y, boolean init_game)
1810 {
1811   int element = Tile[x][y];
1812
1813   switch (element)
1814   {
1815     case EL_SP_MURPHY:
1816     case EL_PLAYER_1:
1817     case EL_PLAYER_2:
1818     case EL_PLAYER_3:
1819     case EL_PLAYER_4:
1820       InitPlayerField(x, y, element, init_game);
1821       break;
1822
1823     case EL_SOKOBAN_FIELD_PLAYER:
1824       element = Tile[x][y] = EL_PLAYER_1;
1825       InitField(x, y, init_game);
1826
1827       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1828       InitField(x, y, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_EMPTY:
1832       IncrementSokobanFieldsNeeded();
1833       break;
1834
1835     case EL_SOKOBAN_OBJECT:
1836       IncrementSokobanObjectsNeeded();
1837       break;
1838
1839     case EL_STONEBLOCK:
1840       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1841         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1842       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1843         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1844       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1845         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1846       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1847         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1848       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1849         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1850       break;
1851
1852     case EL_BUG:
1853     case EL_BUG_RIGHT:
1854     case EL_BUG_UP:
1855     case EL_BUG_LEFT:
1856     case EL_BUG_DOWN:
1857     case EL_SPACESHIP:
1858     case EL_SPACESHIP_RIGHT:
1859     case EL_SPACESHIP_UP:
1860     case EL_SPACESHIP_LEFT:
1861     case EL_SPACESHIP_DOWN:
1862     case EL_BD_BUTTERFLY:
1863     case EL_BD_BUTTERFLY_RIGHT:
1864     case EL_BD_BUTTERFLY_UP:
1865     case EL_BD_BUTTERFLY_LEFT:
1866     case EL_BD_BUTTERFLY_DOWN:
1867     case EL_BD_FIREFLY:
1868     case EL_BD_FIREFLY_RIGHT:
1869     case EL_BD_FIREFLY_UP:
1870     case EL_BD_FIREFLY_LEFT:
1871     case EL_BD_FIREFLY_DOWN:
1872     case EL_PACMAN_RIGHT:
1873     case EL_PACMAN_UP:
1874     case EL_PACMAN_LEFT:
1875     case EL_PACMAN_DOWN:
1876     case EL_YAMYAM:
1877     case EL_YAMYAM_LEFT:
1878     case EL_YAMYAM_RIGHT:
1879     case EL_YAMYAM_UP:
1880     case EL_YAMYAM_DOWN:
1881     case EL_DARK_YAMYAM:
1882     case EL_ROBOT:
1883     case EL_PACMAN:
1884     case EL_SP_SNIKSNAK:
1885     case EL_SP_ELECTRON:
1886     case EL_MOLE:
1887     case EL_MOLE_LEFT:
1888     case EL_MOLE_RIGHT:
1889     case EL_MOLE_UP:
1890     case EL_MOLE_DOWN:
1891     case EL_SPRING_LEFT:
1892     case EL_SPRING_RIGHT:
1893       InitMovDir(x, y);
1894       break;
1895
1896     case EL_AMOEBA_FULL:
1897     case EL_BD_AMOEBA:
1898       InitAmoebaNr(x, y);
1899       break;
1900
1901     case EL_AMOEBA_DROP:
1902       if (y == lev_fieldy - 1)
1903       {
1904         Tile[x][y] = EL_AMOEBA_GROWING;
1905         Store[x][y] = EL_AMOEBA_WET;
1906       }
1907       break;
1908
1909     case EL_DYNAMITE_ACTIVE:
1910     case EL_SP_DISK_RED_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1912     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1913     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1914     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1915       MovDelay[x][y] = 96;
1916       break;
1917
1918     case EL_EM_DYNAMITE_ACTIVE:
1919       MovDelay[x][y] = 32;
1920       break;
1921
1922     case EL_LAMP:
1923       game.lights_still_needed++;
1924       break;
1925
1926     case EL_PENGUIN:
1927       game.friends_still_needed++;
1928       break;
1929
1930     case EL_PIG:
1931     case EL_DRAGON:
1932       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1933       break;
1934
1935     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1944     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1945     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1946     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1947       if (init_game)
1948       {
1949         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1950         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1951         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1952
1953         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1954         {
1955           game.belt_dir[belt_nr] = belt_dir;
1956           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1957         }
1958         else    // more than one switch -- set it like the first switch
1959         {
1960           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1961         }
1962       }
1963       break;
1964
1965     case EL_LIGHT_SWITCH_ACTIVE:
1966       if (init_game)
1967         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1968       break;
1969
1970     case EL_INVISIBLE_STEELWALL:
1971     case EL_INVISIBLE_WALL:
1972     case EL_INVISIBLE_SAND:
1973       if (game.light_time_left > 0 ||
1974           game.lenses_time_left > 0)
1975         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1976       break;
1977
1978     case EL_EMC_MAGIC_BALL:
1979       if (game.ball_active)
1980         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1981       break;
1982
1983     case EL_EMC_MAGIC_BALL_SWITCH:
1984       if (game.ball_active)
1985         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1986       break;
1987
1988     case EL_TRIGGER_PLAYER:
1989     case EL_TRIGGER_ELEMENT:
1990     case EL_TRIGGER_CE_VALUE:
1991     case EL_TRIGGER_CE_SCORE:
1992     case EL_SELF:
1993     case EL_ANY_ELEMENT:
1994     case EL_CURRENT_CE_VALUE:
1995     case EL_CURRENT_CE_SCORE:
1996     case EL_PREV_CE_1:
1997     case EL_PREV_CE_2:
1998     case EL_PREV_CE_3:
1999     case EL_PREV_CE_4:
2000     case EL_PREV_CE_5:
2001     case EL_PREV_CE_6:
2002     case EL_PREV_CE_7:
2003     case EL_PREV_CE_8:
2004     case EL_NEXT_CE_1:
2005     case EL_NEXT_CE_2:
2006     case EL_NEXT_CE_3:
2007     case EL_NEXT_CE_4:
2008     case EL_NEXT_CE_5:
2009     case EL_NEXT_CE_6:
2010     case EL_NEXT_CE_7:
2011     case EL_NEXT_CE_8:
2012       // reference elements should not be used on the playfield
2013       Tile[x][y] = EL_EMPTY;
2014       break;
2015
2016     default:
2017       if (IS_CUSTOM_ELEMENT(element))
2018       {
2019         if (CAN_MOVE(element))
2020           InitMovDir(x, y);
2021
2022         if (!element_info[element].use_last_ce_value || init_game)
2023           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2024       }
2025       else if (IS_GROUP_ELEMENT(element))
2026       {
2027         Tile[x][y] = GetElementFromGroupElement(element);
2028
2029         InitField(x, y, init_game);
2030       }
2031       else if (IS_EMPTY_ELEMENT(element))
2032       {
2033         GfxElementEmpty[x][y] = element;
2034         Tile[x][y] = EL_EMPTY;
2035       }
2036
2037       break;
2038   }
2039
2040   if (!init_game)
2041     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2042 }
2043
2044 static void InitField_WithBug1(int x, int y, boolean init_game)
2045 {
2046   InitField(x, y, init_game);
2047
2048   // not needed to call InitMovDir() -- already done by InitField()!
2049   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2050       CAN_MOVE(Tile[x][y]))
2051     InitMovDir(x, y);
2052 }
2053
2054 static void InitField_WithBug2(int x, int y, boolean init_game)
2055 {
2056   int old_element = Tile[x][y];
2057
2058   InitField(x, y, init_game);
2059
2060   // not needed to call InitMovDir() -- already done by InitField()!
2061   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2062       CAN_MOVE(old_element) &&
2063       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2064     InitMovDir(x, y);
2065
2066   /* this case is in fact a combination of not less than three bugs:
2067      first, it calls InitMovDir() for elements that can move, although this is
2068      already done by InitField(); then, it checks the element that was at this
2069      field _before_ the call to InitField() (which can change it); lastly, it
2070      was not called for "mole with direction" elements, which were treated as
2071      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2072   */
2073 }
2074
2075 static int get_key_element_from_nr(int key_nr)
2076 {
2077   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2078                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2079                           EL_EM_KEY_1 : EL_KEY_1);
2080
2081   return key_base_element + key_nr;
2082 }
2083
2084 static int get_next_dropped_element(struct PlayerInfo *player)
2085 {
2086   return (player->inventory_size > 0 ?
2087           player->inventory_element[player->inventory_size - 1] :
2088           player->inventory_infinite_element != EL_UNDEFINED ?
2089           player->inventory_infinite_element :
2090           player->dynabombs_left > 0 ?
2091           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2092           EL_UNDEFINED);
2093 }
2094
2095 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2096 {
2097   // pos >= 0: get element from bottom of the stack;
2098   // pos <  0: get element from top of the stack
2099
2100   if (pos < 0)
2101   {
2102     int min_inventory_size = -pos;
2103     int inventory_pos = player->inventory_size - min_inventory_size;
2104     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2105
2106     return (player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             player->inventory_infinite_element != EL_UNDEFINED ?
2109             player->inventory_infinite_element :
2110             player->dynabombs_left >= min_dynabombs_left ?
2111             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2112             EL_UNDEFINED);
2113   }
2114   else
2115   {
2116     int min_dynabombs_left = pos + 1;
2117     int min_inventory_size = pos + 1 - player->dynabombs_left;
2118     int inventory_pos = pos - player->dynabombs_left;
2119
2120     return (player->inventory_infinite_element != EL_UNDEFINED ?
2121             player->inventory_infinite_element :
2122             player->dynabombs_left >= min_dynabombs_left ?
2123             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2124             player->inventory_size >= min_inventory_size ?
2125             player->inventory_element[inventory_pos] :
2126             EL_UNDEFINED);
2127   }
2128 }
2129
2130 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2131 {
2132   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2133   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2134   int compare_result;
2135
2136   if (gpo1->sort_priority != gpo2->sort_priority)
2137     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2138   else
2139     compare_result = gpo1->nr - gpo2->nr;
2140
2141   return compare_result;
2142 }
2143
2144 int getPlayerInventorySize(int player_nr)
2145 {
2146   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2147     return game_em.ply[player_nr]->dynamite;
2148   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2149     return game_sp.red_disk_count;
2150   else
2151     return stored_player[player_nr].inventory_size;
2152 }
2153
2154 static void InitGameControlValues(void)
2155 {
2156   int i;
2157
2158   for (i = 0; game_panel_controls[i].nr != -1; i++)
2159   {
2160     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2161     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2162     struct TextPosInfo *pos = gpc->pos;
2163     int nr = gpc->nr;
2164     int type = gpc->type;
2165
2166     if (nr != i)
2167     {
2168       Error("'game_panel_controls' structure corrupted at %d", i);
2169
2170       Fail("this should not happen -- please debug");
2171     }
2172
2173     // force update of game controls after initialization
2174     gpc->value = gpc->last_value = -1;
2175     gpc->frame = gpc->last_frame = -1;
2176     gpc->gfx_frame = -1;
2177
2178     // determine panel value width for later calculation of alignment
2179     if (type == TYPE_INTEGER || type == TYPE_STRING)
2180     {
2181       pos->width = pos->size * getFontWidth(pos->font);
2182       pos->height = getFontHeight(pos->font);
2183     }
2184     else if (type == TYPE_ELEMENT)
2185     {
2186       pos->width = pos->size;
2187       pos->height = pos->size;
2188     }
2189
2190     // fill structure for game panel draw order
2191     gpo->nr = gpc->nr;
2192     gpo->sort_priority = pos->sort_priority;
2193   }
2194
2195   // sort game panel controls according to sort_priority and control number
2196   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2197         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2198 }
2199
2200 static void UpdatePlayfieldElementCount(void)
2201 {
2202   boolean use_element_count = FALSE;
2203   int i, j, x, y;
2204
2205   // first check if it is needed at all to calculate playfield element count
2206   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2207     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2208       use_element_count = TRUE;
2209
2210   if (!use_element_count)
2211     return;
2212
2213   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2214     element_info[i].element_count = 0;
2215
2216   SCAN_PLAYFIELD(x, y)
2217   {
2218     element_info[Tile[x][y]].element_count++;
2219   }
2220
2221   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2222     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2223       if (IS_IN_GROUP(j, i))
2224         element_info[EL_GROUP_START + i].element_count +=
2225           element_info[j].element_count;
2226 }
2227
2228 static void UpdateGameControlValues(void)
2229 {
2230   int i, k;
2231   int time = (game.LevelSolved ?
2232               game.LevelSolved_CountingTime :
2233               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2234               game_em.lev->time :
2235               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2236               game_sp.time_played :
2237               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2238               game_mm.energy_left :
2239               game.no_time_limit ? TimePlayed : TimeLeft);
2240   int score = (game.LevelSolved ?
2241                game.LevelSolved_CountingScore :
2242                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2243                game_em.lev->score :
2244                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2245                game_sp.score :
2246                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2247                game_mm.score :
2248                game.score);
2249   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2250               game_em.lev->gems_needed :
2251               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2252               game_sp.infotrons_still_needed :
2253               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2254               game_mm.kettles_still_needed :
2255               game.gems_still_needed);
2256   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2257                      game_em.lev->gems_needed > 0 :
2258                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2259                      game_sp.infotrons_still_needed > 0 :
2260                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2261                      game_mm.kettles_still_needed > 0 ||
2262                      game_mm.lights_still_needed > 0 :
2263                      game.gems_still_needed > 0 ||
2264                      game.sokoban_fields_still_needed > 0 ||
2265                      game.sokoban_objects_still_needed > 0 ||
2266                      game.lights_still_needed > 0);
2267   int health = (game.LevelSolved ?
2268                 game.LevelSolved_CountingHealth :
2269                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2270                 MM_HEALTH(game_mm.laser_overload_value) :
2271                 game.health);
2272   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2273
2274   UpdatePlayfieldElementCount();
2275
2276   // update game panel control values
2277
2278   // used instead of "level_nr" (for network games)
2279   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2280   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2281
2282   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2283   for (i = 0; i < MAX_NUM_KEYS; i++)
2284     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2285   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2286   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2287
2288   if (game.centered_player_nr == -1)
2289   {
2290     for (i = 0; i < MAX_PLAYERS; i++)
2291     {
2292       // only one player in Supaplex game engine
2293       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2294         break;
2295
2296       for (k = 0; k < MAX_NUM_KEYS; k++)
2297       {
2298         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2299         {
2300           if (game_em.ply[i]->keys & (1 << k))
2301             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2302               get_key_element_from_nr(k);
2303         }
2304         else if (stored_player[i].key[k])
2305           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2306             get_key_element_from_nr(k);
2307       }
2308
2309       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2310         getPlayerInventorySize(i);
2311
2312       if (stored_player[i].num_white_keys > 0)
2313         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2314           EL_DC_KEY_WHITE;
2315
2316       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2317         stored_player[i].num_white_keys;
2318     }
2319   }
2320   else
2321   {
2322     int player_nr = game.centered_player_nr;
2323
2324     for (k = 0; k < MAX_NUM_KEYS; k++)
2325     {
2326       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2327       {
2328         if (game_em.ply[player_nr]->keys & (1 << k))
2329           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2330             get_key_element_from_nr(k);
2331       }
2332       else if (stored_player[player_nr].key[k])
2333         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2334           get_key_element_from_nr(k);
2335     }
2336
2337     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2338       getPlayerInventorySize(player_nr);
2339
2340     if (stored_player[player_nr].num_white_keys > 0)
2341       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2342
2343     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2344       stored_player[player_nr].num_white_keys;
2345   }
2346
2347   // re-arrange keys on game panel, if needed or if defined by style settings
2348   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2349   {
2350     int nr = GAME_PANEL_KEY_1 + i;
2351     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2352     struct TextPosInfo *pos = gpc->pos;
2353
2354     // skip check if key is not in the player's inventory
2355     if (gpc->value == EL_EMPTY)
2356       continue;
2357
2358     // check if keys should be arranged on panel from left to right
2359     if (pos->style == STYLE_LEFTMOST_POSITION)
2360     {
2361       // check previous key positions (left from current key)
2362       for (k = 0; k < i; k++)
2363       {
2364         int nr_new = GAME_PANEL_KEY_1 + k;
2365
2366         if (game_panel_controls[nr_new].value == EL_EMPTY)
2367         {
2368           game_panel_controls[nr_new].value = gpc->value;
2369           gpc->value = EL_EMPTY;
2370
2371           break;
2372         }
2373       }
2374     }
2375
2376     // check if "undefined" keys can be placed at some other position
2377     if (pos->x == -1 && pos->y == -1)
2378     {
2379       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2380
2381       // 1st try: display key at the same position as normal or EM keys
2382       if (game_panel_controls[nr_new].value == EL_EMPTY)
2383       {
2384         game_panel_controls[nr_new].value = gpc->value;
2385       }
2386       else
2387       {
2388         // 2nd try: display key at the next free position in the key panel
2389         for (k = 0; k < STD_NUM_KEYS; k++)
2390         {
2391           nr_new = GAME_PANEL_KEY_1 + k;
2392
2393           if (game_panel_controls[nr_new].value == EL_EMPTY)
2394           {
2395             game_panel_controls[nr_new].value = gpc->value;
2396
2397             break;
2398           }
2399         }
2400       }
2401     }
2402   }
2403
2404   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2405   {
2406     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2407       get_inventory_element_from_pos(local_player, i);
2408     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2409       get_inventory_element_from_pos(local_player, -i - 1);
2410   }
2411
2412   game_panel_controls[GAME_PANEL_SCORE].value = score;
2413   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2414
2415   game_panel_controls[GAME_PANEL_TIME].value = time;
2416
2417   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2418   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2419   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2420
2421   if (level.time == 0)
2422     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2423   else
2424     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2425
2426   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2427   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2428
2429   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2430
2431   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2432     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2433      EL_EMPTY);
2434   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2435     local_player->shield_normal_time_left;
2436   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2437     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2438      EL_EMPTY);
2439   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2440     local_player->shield_deadly_time_left;
2441
2442   game_panel_controls[GAME_PANEL_EXIT].value =
2443     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2444
2445   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2446     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2447   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2448     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2449      EL_EMC_MAGIC_BALL_SWITCH);
2450
2451   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2452     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2453   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2454     game.light_time_left;
2455
2456   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2457     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2458   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2459     game.timegate_time_left;
2460
2461   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2462     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2463
2464   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2465     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2466   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2467     game.lenses_time_left;
2468
2469   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2470     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2471   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2472     game.magnify_time_left;
2473
2474   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2475     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2476      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2477      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2478      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2479      EL_BALLOON_SWITCH_NONE);
2480
2481   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2482     local_player->dynabomb_count;
2483   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2484     local_player->dynabomb_size;
2485   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2486     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2487
2488   game_panel_controls[GAME_PANEL_PENGUINS].value =
2489     game.friends_still_needed;
2490
2491   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2492     game.sokoban_objects_still_needed;
2493   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2494     game.sokoban_fields_still_needed;
2495
2496   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2497     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2498
2499   for (i = 0; i < NUM_BELTS; i++)
2500   {
2501     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2502       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2503        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2504     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2505       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2506   }
2507
2508   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2509     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2510   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2511     game.magic_wall_time_left;
2512
2513   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2514     local_player->gravity;
2515
2516   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2517     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2518
2519   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2520     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2521       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2522        game.panel.element[i].id : EL_UNDEFINED);
2523
2524   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2525     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2526       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2527        element_info[game.panel.element_count[i].id].element_count : 0);
2528
2529   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2530     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2531       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2532        element_info[game.panel.ce_score[i].id].collect_score : 0);
2533
2534   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2535     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2536       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2537        element_info[game.panel.ce_score_element[i].id].collect_score :
2538        EL_UNDEFINED);
2539
2540   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2541   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2542   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2543
2544   // update game panel control frames
2545
2546   for (i = 0; game_panel_controls[i].nr != -1; i++)
2547   {
2548     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2549
2550     if (gpc->type == TYPE_ELEMENT)
2551     {
2552       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2553       {
2554         int last_anim_random_frame = gfx.anim_random_frame;
2555         int element = gpc->value;
2556         int graphic = el2panelimg(element);
2557         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2558                                sync_random_frame : INIT_GFX_RANDOM());
2559
2560         if (gpc->value != gpc->last_value)
2561         {
2562           gpc->gfx_frame = 0;
2563           gpc->gfx_random = init_gfx_random;
2564         }
2565         else
2566         {
2567           gpc->gfx_frame++;
2568
2569           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2570               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2571             gpc->gfx_random = init_gfx_random;
2572         }
2573
2574         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2575           gfx.anim_random_frame = gpc->gfx_random;
2576
2577         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2578           gpc->gfx_frame = element_info[element].collect_score;
2579
2580         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2581
2582         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2583           gfx.anim_random_frame = last_anim_random_frame;
2584       }
2585     }
2586     else if (gpc->type == TYPE_GRAPHIC)
2587     {
2588       if (gpc->graphic != IMG_UNDEFINED)
2589       {
2590         int last_anim_random_frame = gfx.anim_random_frame;
2591         int graphic = gpc->graphic;
2592         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2593                                sync_random_frame : INIT_GFX_RANDOM());
2594
2595         if (gpc->value != gpc->last_value)
2596         {
2597           gpc->gfx_frame = 0;
2598           gpc->gfx_random = init_gfx_random;
2599         }
2600         else
2601         {
2602           gpc->gfx_frame++;
2603
2604           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2605               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2606             gpc->gfx_random = init_gfx_random;
2607         }
2608
2609         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2610           gfx.anim_random_frame = gpc->gfx_random;
2611
2612         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2613
2614         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2615           gfx.anim_random_frame = last_anim_random_frame;
2616       }
2617     }
2618   }
2619 }
2620
2621 static void DisplayGameControlValues(void)
2622 {
2623   boolean redraw_panel = FALSE;
2624   int i;
2625
2626   for (i = 0; game_panel_controls[i].nr != -1; i++)
2627   {
2628     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2629
2630     if (PANEL_DEACTIVATED(gpc->pos))
2631       continue;
2632
2633     if (gpc->value == gpc->last_value &&
2634         gpc->frame == gpc->last_frame)
2635       continue;
2636
2637     redraw_panel = TRUE;
2638   }
2639
2640   if (!redraw_panel)
2641     return;
2642
2643   // copy default game door content to main double buffer
2644
2645   // !!! CHECK AGAIN !!!
2646   SetPanelBackground();
2647   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2648   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2649
2650   // redraw game control buttons
2651   RedrawGameButtons();
2652
2653   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2654
2655   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2656   {
2657     int nr = game_panel_order[i].nr;
2658     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2659     struct TextPosInfo *pos = gpc->pos;
2660     int type = gpc->type;
2661     int value = gpc->value;
2662     int frame = gpc->frame;
2663     int size = pos->size;
2664     int font = pos->font;
2665     boolean draw_masked = pos->draw_masked;
2666     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2667
2668     if (PANEL_DEACTIVATED(pos))
2669       continue;
2670
2671     if (pos->class == get_hash_from_key("extra_panel_items") &&
2672         !setup.prefer_extra_panel_items)
2673       continue;
2674
2675     gpc->last_value = value;
2676     gpc->last_frame = frame;
2677
2678     if (type == TYPE_INTEGER)
2679     {
2680       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2681           nr == GAME_PANEL_TIME)
2682       {
2683         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2684
2685         if (use_dynamic_size)           // use dynamic number of digits
2686         {
2687           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2688           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2689           int size2 = size1 + 1;
2690           int font1 = pos->font;
2691           int font2 = pos->font_alt;
2692
2693           size = (value < value_change ? size1 : size2);
2694           font = (value < value_change ? font1 : font2);
2695         }
2696       }
2697
2698       // correct text size if "digits" is zero or less
2699       if (size <= 0)
2700         size = strlen(int2str(value, size));
2701
2702       // dynamically correct text alignment
2703       pos->width = size * getFontWidth(font);
2704
2705       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2706                   int2str(value, size), font, mask_mode);
2707     }
2708     else if (type == TYPE_ELEMENT)
2709     {
2710       int element, graphic;
2711       Bitmap *src_bitmap;
2712       int src_x, src_y;
2713       int width, height;
2714       int dst_x = PANEL_XPOS(pos);
2715       int dst_y = PANEL_YPOS(pos);
2716
2717       if (value != EL_UNDEFINED && value != EL_EMPTY)
2718       {
2719         element = value;
2720         graphic = el2panelimg(value);
2721
2722 #if 0
2723         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2724               element, EL_NAME(element), size);
2725 #endif
2726
2727         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2728           size = TILESIZE;
2729
2730         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2731                               &src_x, &src_y);
2732
2733         width  = graphic_info[graphic].width  * size / TILESIZE;
2734         height = graphic_info[graphic].height * size / TILESIZE;
2735
2736         if (draw_masked)
2737           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2738                            dst_x, dst_y);
2739         else
2740           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2741                      dst_x, dst_y);
2742       }
2743     }
2744     else if (type == TYPE_GRAPHIC)
2745     {
2746       int graphic        = gpc->graphic;
2747       int graphic_active = gpc->graphic_active;
2748       Bitmap *src_bitmap;
2749       int src_x, src_y;
2750       int width, height;
2751       int dst_x = PANEL_XPOS(pos);
2752       int dst_y = PANEL_YPOS(pos);
2753       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2754                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2755
2756       if (graphic != IMG_UNDEFINED && !skip)
2757       {
2758         if (pos->style == STYLE_REVERSE)
2759           value = 100 - value;
2760
2761         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2762
2763         if (pos->direction & MV_HORIZONTAL)
2764         {
2765           width  = graphic_info[graphic_active].width * value / 100;
2766           height = graphic_info[graphic_active].height;
2767
2768           if (pos->direction == MV_LEFT)
2769           {
2770             src_x += graphic_info[graphic_active].width - width;
2771             dst_x += graphic_info[graphic_active].width - width;
2772           }
2773         }
2774         else
2775         {
2776           width  = graphic_info[graphic_active].width;
2777           height = graphic_info[graphic_active].height * value / 100;
2778
2779           if (pos->direction == MV_UP)
2780           {
2781             src_y += graphic_info[graphic_active].height - height;
2782             dst_y += graphic_info[graphic_active].height - height;
2783           }
2784         }
2785
2786         if (draw_masked)
2787           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2788                            dst_x, dst_y);
2789         else
2790           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2791                      dst_x, dst_y);
2792
2793         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2794
2795         if (pos->direction & MV_HORIZONTAL)
2796         {
2797           if (pos->direction == MV_RIGHT)
2798           {
2799             src_x += width;
2800             dst_x += width;
2801           }
2802           else
2803           {
2804             dst_x = PANEL_XPOS(pos);
2805           }
2806
2807           width = graphic_info[graphic].width - width;
2808         }
2809         else
2810         {
2811           if (pos->direction == MV_DOWN)
2812           {
2813             src_y += height;
2814             dst_y += height;
2815           }
2816           else
2817           {
2818             dst_y = PANEL_YPOS(pos);
2819           }
2820
2821           height = graphic_info[graphic].height - height;
2822         }
2823
2824         if (draw_masked)
2825           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2826                            dst_x, dst_y);
2827         else
2828           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2829                      dst_x, dst_y);
2830       }
2831     }
2832     else if (type == TYPE_STRING)
2833     {
2834       boolean active = (value != 0);
2835       char *state_normal = "off";
2836       char *state_active = "on";
2837       char *state = (active ? state_active : state_normal);
2838       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2839                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2840                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2841                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2842
2843       if (nr == GAME_PANEL_GRAVITY_STATE)
2844       {
2845         int font1 = pos->font;          // (used for normal state)
2846         int font2 = pos->font_alt;      // (used for active state)
2847
2848         font = (active ? font2 : font1);
2849       }
2850
2851       if (s != NULL)
2852       {
2853         char *s_cut;
2854
2855         if (size <= 0)
2856         {
2857           // don't truncate output if "chars" is zero or less
2858           size = strlen(s);
2859
2860           // dynamically correct text alignment
2861           pos->width = size * getFontWidth(font);
2862         }
2863
2864         s_cut = getStringCopyN(s, size);
2865
2866         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2867                     s_cut, font, mask_mode);
2868
2869         free(s_cut);
2870       }
2871     }
2872
2873     redraw_mask |= REDRAW_DOOR_1;
2874   }
2875
2876   SetGameStatus(GAME_MODE_PLAYING);
2877 }
2878
2879 void UpdateAndDisplayGameControlValues(void)
2880 {
2881   if (tape.deactivate_display)
2882     return;
2883
2884   UpdateGameControlValues();
2885   DisplayGameControlValues();
2886 }
2887
2888 void UpdateGameDoorValues(void)
2889 {
2890   UpdateGameControlValues();
2891 }
2892
2893 void DrawGameDoorValues(void)
2894 {
2895   DisplayGameControlValues();
2896 }
2897
2898
2899 // ============================================================================
2900 // InitGameEngine()
2901 // ----------------------------------------------------------------------------
2902 // initialize game engine due to level / tape version number
2903 // ============================================================================
2904
2905 static void InitGameEngine(void)
2906 {
2907   int i, j, k, l, x, y;
2908
2909   // set game engine from tape file when re-playing, else from level file
2910   game.engine_version = (tape.playing ? tape.engine_version :
2911                          level.game_version);
2912
2913   // set single or multi-player game mode (needed for re-playing tapes)
2914   game.team_mode = setup.team_mode;
2915
2916   if (tape.playing)
2917   {
2918     int num_players = 0;
2919
2920     for (i = 0; i < MAX_PLAYERS; i++)
2921       if (tape.player_participates[i])
2922         num_players++;
2923
2924     // multi-player tapes contain input data for more than one player
2925     game.team_mode = (num_players > 1);
2926   }
2927
2928 #if 0
2929   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2930         level.game_version);
2931   Debug("game:init:level", "          tape.file_version   == %06d",
2932         tape.file_version);
2933   Debug("game:init:level", "          tape.game_version   == %06d",
2934         tape.game_version);
2935   Debug("game:init:level", "          tape.engine_version == %06d",
2936         tape.engine_version);
2937   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2938         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2939 #endif
2940
2941   // --------------------------------------------------------------------------
2942   // set flags for bugs and changes according to active game engine version
2943   // --------------------------------------------------------------------------
2944
2945   /*
2946     Summary of bugfix:
2947     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2948
2949     Bug was introduced in version:
2950     2.0.1
2951
2952     Bug was fixed in version:
2953     4.2.0.0
2954
2955     Description:
2956     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2957     but the property "can fall" was missing, which caused some levels to be
2958     unsolvable. This was fixed in version 4.2.0.0.
2959
2960     Affected levels/tapes:
2961     An example for a tape that was fixed by this bugfix is tape 029 from the
2962     level set "rnd_sam_bateman".
2963     The wrong behaviour will still be used for all levels or tapes that were
2964     created/recorded with it. An example for this is tape 023 from the level
2965     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2966   */
2967
2968   boolean use_amoeba_dropping_cannot_fall_bug =
2969     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2970       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2971      (tape.playing &&
2972       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2973       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2974
2975   /*
2976     Summary of bugfix/change:
2977     Fixed move speed of elements entering or leaving magic wall.
2978
2979     Fixed/changed in version:
2980     2.0.1
2981
2982     Description:
2983     Before 2.0.1, move speed of elements entering or leaving magic wall was
2984     twice as fast as it is now.
2985     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2986
2987     Affected levels/tapes:
2988     The first condition is generally needed for all levels/tapes before version
2989     2.0.1, which might use the old behaviour before it was changed; known tapes
2990     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2991     The second condition is an exception from the above case and is needed for
2992     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2993     above, but before it was known that this change would break tapes like the
2994     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2995     although the engine version while recording maybe was before 2.0.1. There
2996     are a lot of tapes that are affected by this exception, like tape 006 from
2997     the level set "rnd_conor_mancone".
2998   */
2999
3000   boolean use_old_move_stepsize_for_magic_wall =
3001     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3002      !(tape.playing &&
3003        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3004        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3005
3006   /*
3007     Summary of bugfix/change:
3008     Fixed handling for custom elements that change when pushed by the player.
3009
3010     Fixed/changed in version:
3011     3.1.0
3012
3013     Description:
3014     Before 3.1.0, custom elements that "change when pushing" changed directly
3015     after the player started pushing them (until then handled in "DigField()").
3016     Since 3.1.0, these custom elements are not changed until the "pushing"
3017     move of the element is finished (now handled in "ContinueMoving()").
3018
3019     Affected levels/tapes:
3020     The first condition is generally needed for all levels/tapes before version
3021     3.1.0, which might use the old behaviour before it was changed; known tapes
3022     that are affected are some tapes from the level set "Walpurgis Gardens" by
3023     Jamie Cullen.
3024     The second condition is an exception from the above case and is needed for
3025     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3026     above (including some development versions of 3.1.0), but before it was
3027     known that this change would break tapes like the above and was fixed in
3028     3.1.1, so that the changed behaviour was active although the engine version
3029     while recording maybe was before 3.1.0. There is at least one tape that is
3030     affected by this exception, which is the tape for the one-level set "Bug
3031     Machine" by Juergen Bonhagen.
3032   */
3033
3034   game.use_change_when_pushing_bug =
3035     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3036      !(tape.playing &&
3037        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3038        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3039
3040   /*
3041     Summary of bugfix/change:
3042     Fixed handling for blocking the field the player leaves when moving.
3043
3044     Fixed/changed in version:
3045     3.1.1
3046
3047     Description:
3048     Before 3.1.1, when "block last field when moving" was enabled, the field
3049     the player is leaving when moving was blocked for the time of the move,
3050     and was directly unblocked afterwards. This resulted in the last field
3051     being blocked for exactly one less than the number of frames of one player
3052     move. Additionally, even when blocking was disabled, the last field was
3053     blocked for exactly one frame.
3054     Since 3.1.1, due to changes in player movement handling, the last field
3055     is not blocked at all when blocking is disabled. When blocking is enabled,
3056     the last field is blocked for exactly the number of frames of one player
3057     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3058     last field is blocked for exactly one more than the number of frames of
3059     one player move.
3060
3061     Affected levels/tapes:
3062     (!!! yet to be determined -- probably many !!!)
3063   */
3064
3065   game.use_block_last_field_bug =
3066     (game.engine_version < VERSION_IDENT(3,1,1,0));
3067
3068   /* various special flags and settings for native Emerald Mine game engine */
3069
3070   game_em.use_single_button =
3071     (game.engine_version > VERSION_IDENT(4,0,0,2));
3072
3073   game_em.use_snap_key_bug =
3074     (game.engine_version < VERSION_IDENT(4,0,1,0));
3075
3076   game_em.use_random_bug =
3077     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3078
3079   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3080
3081   game_em.use_old_explosions            = use_old_em_engine;
3082   game_em.use_old_android               = use_old_em_engine;
3083   game_em.use_old_push_elements         = use_old_em_engine;
3084   game_em.use_old_push_into_acid        = use_old_em_engine;
3085
3086   game_em.use_wrap_around               = !use_old_em_engine;
3087
3088   // --------------------------------------------------------------------------
3089
3090   // set maximal allowed number of custom element changes per game frame
3091   game.max_num_changes_per_frame = 1;
3092
3093   // default scan direction: scan playfield from top/left to bottom/right
3094   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3095
3096   // dynamically adjust element properties according to game engine version
3097   InitElementPropertiesEngine(game.engine_version);
3098
3099   // ---------- initialize special element properties -------------------------
3100
3101   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3102   if (use_amoeba_dropping_cannot_fall_bug)
3103     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3104
3105   // ---------- initialize player's initial move delay ------------------------
3106
3107   // dynamically adjust player properties according to level information
3108   for (i = 0; i < MAX_PLAYERS; i++)
3109     game.initial_move_delay_value[i] =
3110       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3111
3112   // dynamically adjust player properties according to game engine version
3113   for (i = 0; i < MAX_PLAYERS; i++)
3114     game.initial_move_delay[i] =
3115       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3116        game.initial_move_delay_value[i] : 0);
3117
3118   // ---------- initialize player's initial push delay ------------------------
3119
3120   // dynamically adjust player properties according to game engine version
3121   game.initial_push_delay_value =
3122     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3123
3124   // ---------- initialize changing elements ----------------------------------
3125
3126   // initialize changing elements information
3127   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3128   {
3129     struct ElementInfo *ei = &element_info[i];
3130
3131     // this pointer might have been changed in the level editor
3132     ei->change = &ei->change_page[0];
3133
3134     if (!IS_CUSTOM_ELEMENT(i))
3135     {
3136       ei->change->target_element = EL_EMPTY_SPACE;
3137       ei->change->delay_fixed = 0;
3138       ei->change->delay_random = 0;
3139       ei->change->delay_frames = 1;
3140     }
3141
3142     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3143     {
3144       ei->has_change_event[j] = FALSE;
3145
3146       ei->event_page_nr[j] = 0;
3147       ei->event_page[j] = &ei->change_page[0];
3148     }
3149   }
3150
3151   // add changing elements from pre-defined list
3152   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3153   {
3154     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3155     struct ElementInfo *ei = &element_info[ch_delay->element];
3156
3157     ei->change->target_element       = ch_delay->target_element;
3158     ei->change->delay_fixed          = ch_delay->change_delay;
3159
3160     ei->change->pre_change_function  = ch_delay->pre_change_function;
3161     ei->change->change_function      = ch_delay->change_function;
3162     ei->change->post_change_function = ch_delay->post_change_function;
3163
3164     ei->change->can_change = TRUE;
3165     ei->change->can_change_or_has_action = TRUE;
3166
3167     ei->has_change_event[CE_DELAY] = TRUE;
3168
3169     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3170     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3171   }
3172
3173   // ---------- initialize internal run-time variables ------------------------
3174
3175   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3176   {
3177     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3178
3179     for (j = 0; j < ei->num_change_pages; j++)
3180     {
3181       ei->change_page[j].can_change_or_has_action =
3182         (ei->change_page[j].can_change |
3183          ei->change_page[j].has_action);
3184     }
3185   }
3186
3187   // add change events from custom element configuration
3188   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3189   {
3190     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3191
3192     for (j = 0; j < ei->num_change_pages; j++)
3193     {
3194       if (!ei->change_page[j].can_change_or_has_action)
3195         continue;
3196
3197       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3198       {
3199         // only add event page for the first page found with this event
3200         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3201         {
3202           ei->has_change_event[k] = TRUE;
3203
3204           ei->event_page_nr[k] = j;
3205           ei->event_page[k] = &ei->change_page[j];
3206         }
3207       }
3208     }
3209   }
3210
3211   // ---------- initialize reference elements in change conditions ------------
3212
3213   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3214   {
3215     int element = EL_CUSTOM_START + i;
3216     struct ElementInfo *ei = &element_info[element];
3217
3218     for (j = 0; j < ei->num_change_pages; j++)
3219     {
3220       int trigger_element = ei->change_page[j].initial_trigger_element;
3221
3222       if (trigger_element >= EL_PREV_CE_8 &&
3223           trigger_element <= EL_NEXT_CE_8)
3224         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3225
3226       ei->change_page[j].trigger_element = trigger_element;
3227     }
3228   }
3229
3230   // ---------- initialize run-time trigger player and element ----------------
3231
3232   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3233   {
3234     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3235
3236     for (j = 0; j < ei->num_change_pages; j++)
3237     {
3238       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3239       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3240       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3241       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3242       ei->change_page[j].actual_trigger_ce_value = 0;
3243       ei->change_page[j].actual_trigger_ce_score = 0;
3244     }
3245   }
3246
3247   // ---------- initialize trigger events -------------------------------------
3248
3249   // initialize trigger events information
3250   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3251     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3252       trigger_events[i][j] = FALSE;
3253
3254   // add trigger events from element change event properties
3255   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3256   {
3257     struct ElementInfo *ei = &element_info[i];
3258
3259     for (j = 0; j < ei->num_change_pages; j++)
3260     {
3261       if (!ei->change_page[j].can_change_or_has_action)
3262         continue;
3263
3264       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3265       {
3266         int trigger_element = ei->change_page[j].trigger_element;
3267
3268         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3269         {
3270           if (ei->change_page[j].has_event[k])
3271           {
3272             if (IS_GROUP_ELEMENT(trigger_element))
3273             {
3274               struct ElementGroupInfo *group =
3275                 element_info[trigger_element].group;
3276
3277               for (l = 0; l < group->num_elements_resolved; l++)
3278                 trigger_events[group->element_resolved[l]][k] = TRUE;
3279             }
3280             else if (trigger_element == EL_ANY_ELEMENT)
3281               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3282                 trigger_events[l][k] = TRUE;
3283             else
3284               trigger_events[trigger_element][k] = TRUE;
3285           }
3286         }
3287       }
3288     }
3289   }
3290
3291   // ---------- initialize push delay -----------------------------------------
3292
3293   // initialize push delay values to default
3294   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3295   {
3296     if (!IS_CUSTOM_ELEMENT(i))
3297     {
3298       // set default push delay values (corrected since version 3.0.7-1)
3299       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3300       {
3301         element_info[i].push_delay_fixed = 2;
3302         element_info[i].push_delay_random = 8;
3303       }
3304       else
3305       {
3306         element_info[i].push_delay_fixed = 8;
3307         element_info[i].push_delay_random = 8;
3308       }
3309     }
3310   }
3311
3312   // set push delay value for certain elements from pre-defined list
3313   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3314   {
3315     int e = push_delay_list[i].element;
3316
3317     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3318     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3319   }
3320
3321   // set push delay value for Supaplex elements for newer engine versions
3322   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3323   {
3324     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3325     {
3326       if (IS_SP_ELEMENT(i))
3327       {
3328         // set SP push delay to just enough to push under a falling zonk
3329         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3330
3331         element_info[i].push_delay_fixed  = delay;
3332         element_info[i].push_delay_random = 0;
3333       }
3334     }
3335   }
3336
3337   // ---------- initialize move stepsize --------------------------------------
3338
3339   // initialize move stepsize values to default
3340   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3341     if (!IS_CUSTOM_ELEMENT(i))
3342       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3343
3344   // set move stepsize value for certain elements from pre-defined list
3345   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3346   {
3347     int e = move_stepsize_list[i].element;
3348
3349     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3350
3351     // set move stepsize value for certain elements for older engine versions
3352     if (use_old_move_stepsize_for_magic_wall)
3353     {
3354       if (e == EL_MAGIC_WALL_FILLING ||
3355           e == EL_MAGIC_WALL_EMPTYING ||
3356           e == EL_BD_MAGIC_WALL_FILLING ||
3357           e == EL_BD_MAGIC_WALL_EMPTYING)
3358         element_info[e].move_stepsize *= 2;
3359     }
3360   }
3361
3362   // ---------- initialize collect score --------------------------------------
3363
3364   // initialize collect score values for custom elements from initial value
3365   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3366     if (IS_CUSTOM_ELEMENT(i))
3367       element_info[i].collect_score = element_info[i].collect_score_initial;
3368
3369   // ---------- initialize collect count --------------------------------------
3370
3371   // initialize collect count values for non-custom elements
3372   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3373     if (!IS_CUSTOM_ELEMENT(i))
3374       element_info[i].collect_count_initial = 0;
3375
3376   // add collect count values for all elements from pre-defined list
3377   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3378     element_info[collect_count_list[i].element].collect_count_initial =
3379       collect_count_list[i].count;
3380
3381   // ---------- initialize access direction -----------------------------------
3382
3383   // initialize access direction values to default (access from every side)
3384   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3385     if (!IS_CUSTOM_ELEMENT(i))
3386       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3387
3388   // set access direction value for certain elements from pre-defined list
3389   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3390     element_info[access_direction_list[i].element].access_direction =
3391       access_direction_list[i].direction;
3392
3393   // ---------- initialize explosion content ----------------------------------
3394   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3395   {
3396     if (IS_CUSTOM_ELEMENT(i))
3397       continue;
3398
3399     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3400     {
3401       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3402
3403       element_info[i].content.e[x][y] =
3404         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3405          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3406          i == EL_PLAYER_3 ? EL_EMERALD :
3407          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3408          i == EL_MOLE ? EL_EMERALD_RED :
3409          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3410          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3411          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3412          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3413          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3414          i == EL_WALL_EMERALD ? EL_EMERALD :
3415          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3416          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3417          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3418          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3419          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3420          i == EL_WALL_PEARL ? EL_PEARL :
3421          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3422          EL_EMPTY);
3423     }
3424   }
3425
3426   // ---------- initialize recursion detection --------------------------------
3427   recursion_loop_depth = 0;
3428   recursion_loop_detected = FALSE;
3429   recursion_loop_element = EL_UNDEFINED;
3430
3431   // ---------- initialize graphics engine ------------------------------------
3432   game.scroll_delay_value =
3433     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3434      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3435      !setup.forced_scroll_delay           ? 0 :
3436      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3437   game.scroll_delay_value =
3438     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3439
3440   // ---------- initialize game engine snapshots ------------------------------
3441   for (i = 0; i < MAX_PLAYERS; i++)
3442     game.snapshot.last_action[i] = 0;
3443   game.snapshot.changed_action = FALSE;
3444   game.snapshot.collected_item = FALSE;
3445   game.snapshot.mode =
3446     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3447      SNAPSHOT_MODE_EVERY_STEP :
3448      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3449      SNAPSHOT_MODE_EVERY_MOVE :
3450      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3451      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3452   game.snapshot.save_snapshot = FALSE;
3453
3454   // ---------- initialize level time for Supaplex engine ---------------------
3455   // Supaplex levels with time limit currently unsupported -- should be added
3456   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3457     level.time = 0;
3458
3459   // ---------- initialize flags for handling game actions --------------------
3460
3461   // set flags for game actions to default values
3462   game.use_key_actions = TRUE;
3463   game.use_mouse_actions = FALSE;
3464
3465   // when using Mirror Magic game engine, handle mouse events only
3466   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3467   {
3468     game.use_key_actions = FALSE;
3469     game.use_mouse_actions = TRUE;
3470   }
3471
3472   // check for custom elements with mouse click events
3473   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3474   {
3475     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3476     {
3477       int element = EL_CUSTOM_START + i;
3478
3479       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3480           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3481           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3482           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3483         game.use_mouse_actions = TRUE;
3484     }
3485   }
3486 }
3487
3488 static int get_num_special_action(int element, int action_first,
3489                                   int action_last)
3490 {
3491   int num_special_action = 0;
3492   int i, j;
3493
3494   for (i = action_first; i <= action_last; i++)
3495   {
3496     boolean found = FALSE;
3497
3498     for (j = 0; j < NUM_DIRECTIONS; j++)
3499       if (el_act_dir2img(element, i, j) !=
3500           el_act_dir2img(element, ACTION_DEFAULT, j))
3501         found = TRUE;
3502
3503     if (found)
3504       num_special_action++;
3505     else
3506       break;
3507   }
3508
3509   return num_special_action;
3510 }
3511
3512
3513 // ============================================================================
3514 // InitGame()
3515 // ----------------------------------------------------------------------------
3516 // initialize and start new game
3517 // ============================================================================
3518
3519 #if DEBUG_INIT_PLAYER
3520 static void DebugPrintPlayerStatus(char *message)
3521 {
3522   int i;
3523
3524   if (!options.debug)
3525     return;
3526
3527   Debug("game:init:player", "%s:", message);
3528
3529   for (i = 0; i < MAX_PLAYERS; i++)
3530   {
3531     struct PlayerInfo *player = &stored_player[i];
3532
3533     Debug("game:init:player",
3534           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3535           i + 1,
3536           player->present,
3537           player->connected,
3538           player->connected_locally,
3539           player->connected_network,
3540           player->active,
3541           (local_player == player ? " (local player)" : ""));
3542   }
3543 }
3544 #endif
3545
3546 void InitGame(void)
3547 {
3548   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3549   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3550   int fade_mask = REDRAW_FIELD;
3551
3552   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3553   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3554   int initial_move_dir = MV_DOWN;
3555   int i, j, x, y;
3556
3557   // required here to update video display before fading (FIX THIS)
3558   DrawMaskedBorder(REDRAW_DOOR_2);
3559
3560   if (!game.restart_level)
3561     CloseDoor(DOOR_CLOSE_1);
3562
3563   SetGameStatus(GAME_MODE_PLAYING);
3564
3565   if (level_editor_test_game)
3566     FadeSkipNextFadeOut();
3567   else
3568     FadeSetEnterScreen();
3569
3570   if (CheckFadeAll())
3571     fade_mask = REDRAW_ALL;
3572
3573   FadeLevelSoundsAndMusic();
3574
3575   ExpireSoundLoops(TRUE);
3576
3577   FadeOut(fade_mask);
3578
3579   if (level_editor_test_game)
3580     FadeSkipNextFadeIn();
3581
3582   // needed if different viewport properties defined for playing
3583   ChangeViewportPropertiesIfNeeded();
3584
3585   ClearField();
3586
3587   DrawCompleteVideoDisplay();
3588
3589   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3590
3591   InitGameEngine();
3592   InitGameControlValues();
3593
3594   if (tape.recording)
3595   {
3596     // initialize tape actions from game when recording tape
3597     tape.use_key_actions   = game.use_key_actions;
3598     tape.use_mouse_actions = game.use_mouse_actions;
3599
3600     // initialize visible playfield size when recording tape (for team mode)
3601     tape.scr_fieldx = SCR_FIELDX;
3602     tape.scr_fieldy = SCR_FIELDY;
3603   }
3604
3605   // don't play tapes over network
3606   network_playing = (network.enabled && !tape.playing);
3607
3608   for (i = 0; i < MAX_PLAYERS; i++)
3609   {
3610     struct PlayerInfo *player = &stored_player[i];
3611
3612     player->index_nr = i;
3613     player->index_bit = (1 << i);
3614     player->element_nr = EL_PLAYER_1 + i;
3615
3616     player->present = FALSE;
3617     player->active = FALSE;
3618     player->mapped = FALSE;
3619
3620     player->killed = FALSE;
3621     player->reanimated = FALSE;
3622     player->buried = FALSE;
3623
3624     player->action = 0;
3625     player->effective_action = 0;
3626     player->programmed_action = 0;
3627     player->snap_action = 0;
3628
3629     player->mouse_action.lx = 0;
3630     player->mouse_action.ly = 0;
3631     player->mouse_action.button = 0;
3632     player->mouse_action.button_hint = 0;
3633
3634     player->effective_mouse_action.lx = 0;
3635     player->effective_mouse_action.ly = 0;
3636     player->effective_mouse_action.button = 0;
3637     player->effective_mouse_action.button_hint = 0;
3638
3639     for (j = 0; j < MAX_NUM_KEYS; j++)
3640       player->key[j] = FALSE;
3641
3642     player->num_white_keys = 0;
3643
3644     player->dynabomb_count = 0;
3645     player->dynabomb_size = 1;
3646     player->dynabombs_left = 0;
3647     player->dynabomb_xl = FALSE;
3648
3649     player->MovDir = initial_move_dir;
3650     player->MovPos = 0;
3651     player->GfxPos = 0;
3652     player->GfxDir = initial_move_dir;
3653     player->GfxAction = ACTION_DEFAULT;
3654     player->Frame = 0;
3655     player->StepFrame = 0;
3656
3657     player->initial_element = player->element_nr;
3658     player->artwork_element =
3659       (level.use_artwork_element[i] ? level.artwork_element[i] :
3660        player->element_nr);
3661     player->use_murphy = FALSE;
3662
3663     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3664     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3665
3666     player->gravity = level.initial_player_gravity[i];
3667
3668     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3669
3670     player->actual_frame_counter = 0;
3671
3672     player->step_counter = 0;
3673
3674     player->last_move_dir = initial_move_dir;
3675
3676     player->is_active = FALSE;
3677
3678     player->is_waiting = FALSE;
3679     player->is_moving = FALSE;
3680     player->is_auto_moving = FALSE;
3681     player->is_digging = FALSE;
3682     player->is_snapping = FALSE;
3683     player->is_collecting = FALSE;
3684     player->is_pushing = FALSE;
3685     player->is_switching = FALSE;
3686     player->is_dropping = FALSE;
3687     player->is_dropping_pressed = FALSE;
3688
3689     player->is_bored = FALSE;
3690     player->is_sleeping = FALSE;
3691
3692     player->was_waiting = TRUE;
3693     player->was_moving = FALSE;
3694     player->was_snapping = FALSE;
3695     player->was_dropping = FALSE;
3696
3697     player->force_dropping = FALSE;
3698
3699     player->frame_counter_bored = -1;
3700     player->frame_counter_sleeping = -1;
3701
3702     player->anim_delay_counter = 0;
3703     player->post_delay_counter = 0;
3704
3705     player->dir_waiting = initial_move_dir;
3706     player->action_waiting = ACTION_DEFAULT;
3707     player->last_action_waiting = ACTION_DEFAULT;
3708     player->special_action_bored = ACTION_DEFAULT;
3709     player->special_action_sleeping = ACTION_DEFAULT;
3710
3711     player->switch_x = -1;
3712     player->switch_y = -1;
3713
3714     player->drop_x = -1;
3715     player->drop_y = -1;
3716
3717     player->show_envelope = 0;
3718
3719     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3720
3721     player->push_delay       = -1;      // initialized when pushing starts
3722     player->push_delay_value = game.initial_push_delay_value;
3723
3724     player->drop_delay = 0;
3725     player->drop_pressed_delay = 0;
3726
3727     player->last_jx = -1;
3728     player->last_jy = -1;
3729     player->jx = -1;
3730     player->jy = -1;
3731
3732     player->shield_normal_time_left = 0;
3733     player->shield_deadly_time_left = 0;
3734
3735     player->last_removed_element = EL_UNDEFINED;
3736
3737     player->inventory_infinite_element = EL_UNDEFINED;
3738     player->inventory_size = 0;
3739
3740     if (level.use_initial_inventory[i])
3741     {
3742       for (j = 0; j < level.initial_inventory_size[i]; j++)
3743       {
3744         int element = level.initial_inventory_content[i][j];
3745         int collect_count = element_info[element].collect_count_initial;
3746         int k;
3747
3748         if (!IS_CUSTOM_ELEMENT(element))
3749           collect_count = 1;
3750
3751         if (collect_count == 0)
3752           player->inventory_infinite_element = element;
3753         else
3754           for (k = 0; k < collect_count; k++)
3755             if (player->inventory_size < MAX_INVENTORY_SIZE)
3756               player->inventory_element[player->inventory_size++] = element;
3757       }
3758     }
3759
3760     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3761     SnapField(player, 0, 0);
3762
3763     map_player_action[i] = i;
3764   }
3765
3766   network_player_action_received = FALSE;
3767
3768   // initial null action
3769   if (network_playing)
3770     SendToServer_MovePlayer(MV_NONE);
3771
3772   FrameCounter = 0;
3773   TimeFrames = 0;
3774   TimePlayed = 0;
3775   TimeLeft = level.time;
3776   TapeTime = 0;
3777
3778   ScreenMovDir = MV_NONE;
3779   ScreenMovPos = 0;
3780   ScreenGfxPos = 0;
3781
3782   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3783
3784   game.robot_wheel_x = -1;
3785   game.robot_wheel_y = -1;
3786
3787   game.exit_x = -1;
3788   game.exit_y = -1;
3789
3790   game.all_players_gone = FALSE;
3791
3792   game.LevelSolved = FALSE;
3793   game.GameOver = FALSE;
3794
3795   game.GamePlayed = !tape.playing;
3796
3797   game.LevelSolved_GameWon = FALSE;
3798   game.LevelSolved_GameEnd = FALSE;
3799   game.LevelSolved_SaveTape = FALSE;
3800   game.LevelSolved_SaveScore = FALSE;
3801
3802   game.LevelSolved_CountingTime = 0;
3803   game.LevelSolved_CountingScore = 0;
3804   game.LevelSolved_CountingHealth = 0;
3805
3806   game.panel.active = TRUE;
3807
3808   game.no_time_limit = (level.time == 0);
3809
3810   game.yamyam_content_nr = 0;
3811   game.robot_wheel_active = FALSE;
3812   game.magic_wall_active = FALSE;
3813   game.magic_wall_time_left = 0;
3814   game.light_time_left = 0;
3815   game.timegate_time_left = 0;
3816   game.switchgate_pos = 0;
3817   game.wind_direction = level.wind_direction_initial;
3818
3819   game.time_final = 0;
3820   game.score_time_final = 0;
3821
3822   game.score = 0;
3823   game.score_final = 0;
3824
3825   game.health = MAX_HEALTH;
3826   game.health_final = MAX_HEALTH;
3827
3828   game.gems_still_needed = level.gems_needed;
3829   game.sokoban_fields_still_needed = 0;
3830   game.sokoban_objects_still_needed = 0;
3831   game.lights_still_needed = 0;
3832   game.players_still_needed = 0;
3833   game.friends_still_needed = 0;
3834
3835   game.lenses_time_left = 0;
3836   game.magnify_time_left = 0;
3837
3838   game.ball_active = level.ball_active_initial;
3839   game.ball_content_nr = 0;
3840
3841   game.explosions_delayed = TRUE;
3842
3843   game.envelope_active = FALSE;
3844
3845   for (i = 0; i < NUM_BELTS; i++)
3846   {
3847     game.belt_dir[i] = MV_NONE;
3848     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3849   }
3850
3851   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3852     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3853
3854 #if DEBUG_INIT_PLAYER
3855   DebugPrintPlayerStatus("Player status at level initialization");
3856 #endif
3857
3858   SCAN_PLAYFIELD(x, y)
3859   {
3860     Tile[x][y] = Last[x][y] = level.field[x][y];
3861     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3862     ChangeDelay[x][y] = 0;
3863     ChangePage[x][y] = -1;
3864     CustomValue[x][y] = 0;              // initialized in InitField()
3865     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3866     AmoebaNr[x][y] = 0;
3867     WasJustMoving[x][y] = 0;
3868     WasJustFalling[x][y] = 0;
3869     CheckCollision[x][y] = 0;
3870     CheckImpact[x][y] = 0;
3871     Stop[x][y] = FALSE;
3872     Pushed[x][y] = FALSE;
3873
3874     ChangeCount[x][y] = 0;
3875     ChangeEvent[x][y] = -1;
3876
3877     ExplodePhase[x][y] = 0;
3878     ExplodeDelay[x][y] = 0;
3879     ExplodeField[x][y] = EX_TYPE_NONE;
3880
3881     RunnerVisit[x][y] = 0;
3882     PlayerVisit[x][y] = 0;
3883
3884     GfxFrame[x][y] = 0;
3885     GfxRandom[x][y] = INIT_GFX_RANDOM();
3886     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3887     GfxElement[x][y] = EL_UNDEFINED;
3888     GfxElementEmpty[x][y] = EL_EMPTY;
3889     GfxAction[x][y] = ACTION_DEFAULT;
3890     GfxDir[x][y] = MV_NONE;
3891     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3892   }
3893
3894   SCAN_PLAYFIELD(x, y)
3895   {
3896     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3897       emulate_bd = FALSE;
3898     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3899       emulate_sp = FALSE;
3900
3901     InitField(x, y, TRUE);
3902
3903     ResetGfxAnimation(x, y);
3904   }
3905
3906   InitBeltMovement();
3907
3908   for (i = 0; i < MAX_PLAYERS; i++)
3909   {
3910     struct PlayerInfo *player = &stored_player[i];
3911
3912     // set number of special actions for bored and sleeping animation
3913     player->num_special_action_bored =
3914       get_num_special_action(player->artwork_element,
3915                              ACTION_BORING_1, ACTION_BORING_LAST);
3916     player->num_special_action_sleeping =
3917       get_num_special_action(player->artwork_element,
3918                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3919   }
3920
3921   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3922                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3923
3924   // initialize type of slippery elements
3925   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3926   {
3927     if (!IS_CUSTOM_ELEMENT(i))
3928     {
3929       // default: elements slip down either to the left or right randomly
3930       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3931
3932       // SP style elements prefer to slip down on the left side
3933       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3934         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3935
3936       // BD style elements prefer to slip down on the left side
3937       if (game.emulation == EMU_BOULDERDASH)
3938         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3939     }
3940   }
3941
3942   // initialize explosion and ignition delay
3943   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3944   {
3945     if (!IS_CUSTOM_ELEMENT(i))
3946     {
3947       int num_phase = 8;
3948       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3949                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3950                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3951       int last_phase = (num_phase + 1) * delay;
3952       int half_phase = (num_phase / 2) * delay;
3953
3954       element_info[i].explosion_delay = last_phase - 1;
3955       element_info[i].ignition_delay = half_phase;
3956
3957       if (i == EL_BLACK_ORB)
3958         element_info[i].ignition_delay = 1;
3959     }
3960   }
3961
3962   // correct non-moving belts to start moving left
3963   for (i = 0; i < NUM_BELTS; i++)
3964     if (game.belt_dir[i] == MV_NONE)
3965       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3966
3967 #if USE_NEW_PLAYER_ASSIGNMENTS
3968   // use preferred player also in local single-player mode
3969   if (!network.enabled && !game.team_mode)
3970   {
3971     int new_index_nr = setup.network_player_nr;
3972
3973     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3974     {
3975       for (i = 0; i < MAX_PLAYERS; i++)
3976         stored_player[i].connected_locally = FALSE;
3977
3978       stored_player[new_index_nr].connected_locally = TRUE;
3979     }
3980   }
3981
3982   for (i = 0; i < MAX_PLAYERS; i++)
3983   {
3984     stored_player[i].connected = FALSE;
3985
3986     // in network game mode, the local player might not be the first player
3987     if (stored_player[i].connected_locally)
3988       local_player = &stored_player[i];
3989   }
3990
3991   if (!network.enabled)
3992     local_player->connected = TRUE;
3993
3994   if (tape.playing)
3995   {
3996     for (i = 0; i < MAX_PLAYERS; i++)
3997       stored_player[i].connected = tape.player_participates[i];
3998   }
3999   else if (network.enabled)
4000   {
4001     // add team mode players connected over the network (needed for correct
4002     // assignment of player figures from level to locally playing players)
4003
4004     for (i = 0; i < MAX_PLAYERS; i++)
4005       if (stored_player[i].connected_network)
4006         stored_player[i].connected = TRUE;
4007   }
4008   else if (game.team_mode)
4009   {
4010     // try to guess locally connected team mode players (needed for correct
4011     // assignment of player figures from level to locally playing players)
4012
4013     for (i = 0; i < MAX_PLAYERS; i++)
4014       if (setup.input[i].use_joystick ||
4015           setup.input[i].key.left != KSYM_UNDEFINED)
4016         stored_player[i].connected = TRUE;
4017   }
4018
4019 #if DEBUG_INIT_PLAYER
4020   DebugPrintPlayerStatus("Player status after level initialization");
4021 #endif
4022
4023 #if DEBUG_INIT_PLAYER
4024   Debug("game:init:player", "Reassigning players ...");
4025 #endif
4026
4027   // check if any connected player was not found in playfield
4028   for (i = 0; i < MAX_PLAYERS; i++)
4029   {
4030     struct PlayerInfo *player = &stored_player[i];
4031
4032     if (player->connected && !player->present)
4033     {
4034       struct PlayerInfo *field_player = NULL;
4035
4036 #if DEBUG_INIT_PLAYER
4037       Debug("game:init:player",
4038             "- looking for field player for player %d ...", i + 1);
4039 #endif
4040
4041       // assign first free player found that is present in the playfield
4042
4043       // first try: look for unmapped playfield player that is not connected
4044       for (j = 0; j < MAX_PLAYERS; j++)
4045         if (field_player == NULL &&
4046             stored_player[j].present &&
4047             !stored_player[j].mapped &&
4048             !stored_player[j].connected)
4049           field_player = &stored_player[j];
4050
4051       // second try: look for *any* unmapped playfield player
4052       for (j = 0; j < MAX_PLAYERS; j++)
4053         if (field_player == NULL &&
4054             stored_player[j].present &&
4055             !stored_player[j].mapped)
4056           field_player = &stored_player[j];
4057
4058       if (field_player != NULL)
4059       {
4060         int jx = field_player->jx, jy = field_player->jy;
4061
4062 #if DEBUG_INIT_PLAYER
4063         Debug("game:init:player", "- found player %d",
4064               field_player->index_nr + 1);
4065 #endif
4066
4067         player->present = FALSE;
4068         player->active = FALSE;
4069
4070         field_player->present = TRUE;
4071         field_player->active = TRUE;
4072
4073         /*
4074         player->initial_element = field_player->initial_element;
4075         player->artwork_element = field_player->artwork_element;
4076
4077         player->block_last_field       = field_player->block_last_field;
4078         player->block_delay_adjustment = field_player->block_delay_adjustment;
4079         */
4080
4081         StorePlayer[jx][jy] = field_player->element_nr;
4082
4083         field_player->jx = field_player->last_jx = jx;
4084         field_player->jy = field_player->last_jy = jy;
4085
4086         if (local_player == player)
4087           local_player = field_player;
4088
4089         map_player_action[field_player->index_nr] = i;
4090
4091         field_player->mapped = TRUE;
4092
4093 #if DEBUG_INIT_PLAYER
4094         Debug("game:init:player", "- map_player_action[%d] == %d",
4095               field_player->index_nr + 1, i + 1);
4096 #endif
4097       }
4098     }
4099
4100     if (player->connected && player->present)
4101       player->mapped = TRUE;
4102   }
4103
4104 #if DEBUG_INIT_PLAYER
4105   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4106 #endif
4107
4108 #else
4109
4110   // check if any connected player was not found in playfield
4111   for (i = 0; i < MAX_PLAYERS; i++)
4112   {
4113     struct PlayerInfo *player = &stored_player[i];
4114
4115     if (player->connected && !player->present)
4116     {
4117       for (j = 0; j < MAX_PLAYERS; j++)
4118       {
4119         struct PlayerInfo *field_player = &stored_player[j];
4120         int jx = field_player->jx, jy = field_player->jy;
4121
4122         // assign first free player found that is present in the playfield
4123         if (field_player->present && !field_player->connected)
4124         {
4125           player->present = TRUE;
4126           player->active = TRUE;
4127
4128           field_player->present = FALSE;
4129           field_player->active = FALSE;
4130
4131           player->initial_element = field_player->initial_element;
4132           player->artwork_element = field_player->artwork_element;
4133
4134           player->block_last_field       = field_player->block_last_field;
4135           player->block_delay_adjustment = field_player->block_delay_adjustment;
4136
4137           StorePlayer[jx][jy] = player->element_nr;
4138
4139           player->jx = player->last_jx = jx;
4140           player->jy = player->last_jy = jy;
4141
4142           break;
4143         }
4144       }
4145     }
4146   }
4147 #endif
4148
4149 #if 0
4150   Debug("game:init:player", "local_player->present == %d",
4151         local_player->present);
4152 #endif
4153
4154   // set focus to local player for network games, else to all players
4155   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4156   game.centered_player_nr_next = game.centered_player_nr;
4157   game.set_centered_player = FALSE;
4158   game.set_centered_player_wrap = FALSE;
4159
4160   if (network_playing && tape.recording)
4161   {
4162     // store client dependent player focus when recording network games
4163     tape.centered_player_nr_next = game.centered_player_nr_next;
4164     tape.set_centered_player = TRUE;
4165   }
4166
4167   if (tape.playing)
4168   {
4169     // when playing a tape, eliminate all players who do not participate
4170
4171 #if USE_NEW_PLAYER_ASSIGNMENTS
4172
4173     if (!game.team_mode)
4174     {
4175       for (i = 0; i < MAX_PLAYERS; i++)
4176       {
4177         if (stored_player[i].active &&
4178             !tape.player_participates[map_player_action[i]])
4179         {
4180           struct PlayerInfo *player = &stored_player[i];
4181           int jx = player->jx, jy = player->jy;
4182
4183 #if DEBUG_INIT_PLAYER
4184           Debug("game:init:player", "Removing player %d at (%d, %d)",
4185                 i + 1, jx, jy);
4186 #endif
4187
4188           player->active = FALSE;
4189           StorePlayer[jx][jy] = 0;
4190           Tile[jx][jy] = EL_EMPTY;
4191         }
4192       }
4193     }
4194
4195 #else
4196
4197     for (i = 0; i < MAX_PLAYERS; i++)
4198     {
4199       if (stored_player[i].active &&
4200           !tape.player_participates[i])
4201       {
4202         struct PlayerInfo *player = &stored_player[i];
4203         int jx = player->jx, jy = player->jy;
4204
4205         player->active = FALSE;
4206         StorePlayer[jx][jy] = 0;
4207         Tile[jx][jy] = EL_EMPTY;
4208       }
4209     }
4210 #endif
4211   }
4212   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4213   {
4214     // when in single player mode, eliminate all but the local player
4215
4216     for (i = 0; i < MAX_PLAYERS; i++)
4217     {
4218       struct PlayerInfo *player = &stored_player[i];
4219
4220       if (player->active && player != local_player)
4221       {
4222         int jx = player->jx, jy = player->jy;
4223
4224         player->active = FALSE;
4225         player->present = FALSE;
4226
4227         StorePlayer[jx][jy] = 0;
4228         Tile[jx][jy] = EL_EMPTY;
4229       }
4230     }
4231   }
4232
4233   for (i = 0; i < MAX_PLAYERS; i++)
4234     if (stored_player[i].active)
4235       game.players_still_needed++;
4236
4237   if (level.solved_by_one_player)
4238     game.players_still_needed = 1;
4239
4240   // when recording the game, store which players take part in the game
4241   if (tape.recording)
4242   {
4243 #if USE_NEW_PLAYER_ASSIGNMENTS
4244     for (i = 0; i < MAX_PLAYERS; i++)
4245       if (stored_player[i].connected)
4246         tape.player_participates[i] = TRUE;
4247 #else
4248     for (i = 0; i < MAX_PLAYERS; i++)
4249       if (stored_player[i].active)
4250         tape.player_participates[i] = TRUE;
4251 #endif
4252   }
4253
4254 #if DEBUG_INIT_PLAYER
4255   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4256 #endif
4257
4258   if (BorderElement == EL_EMPTY)
4259   {
4260     SBX_Left = 0;
4261     SBX_Right = lev_fieldx - SCR_FIELDX;
4262     SBY_Upper = 0;
4263     SBY_Lower = lev_fieldy - SCR_FIELDY;
4264   }
4265   else
4266   {
4267     SBX_Left = -1;
4268     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4269     SBY_Upper = -1;
4270     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4271   }
4272
4273   if (full_lev_fieldx <= SCR_FIELDX)
4274     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4275   if (full_lev_fieldy <= SCR_FIELDY)
4276     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4277
4278   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4279     SBX_Left--;
4280   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4281     SBY_Upper--;
4282
4283   // if local player not found, look for custom element that might create
4284   // the player (make some assumptions about the right custom element)
4285   if (!local_player->present)
4286   {
4287     int start_x = 0, start_y = 0;
4288     int found_rating = 0;
4289     int found_element = EL_UNDEFINED;
4290     int player_nr = local_player->index_nr;
4291
4292     SCAN_PLAYFIELD(x, y)
4293     {
4294       int element = Tile[x][y];
4295       int content;
4296       int xx, yy;
4297       boolean is_player;
4298
4299       if (level.use_start_element[player_nr] &&
4300           level.start_element[player_nr] == element &&
4301           found_rating < 4)
4302       {
4303         start_x = x;
4304         start_y = y;
4305
4306         found_rating = 4;
4307         found_element = element;
4308       }
4309
4310       if (!IS_CUSTOM_ELEMENT(element))
4311         continue;
4312
4313       if (CAN_CHANGE(element))
4314       {
4315         for (i = 0; i < element_info[element].num_change_pages; i++)
4316         {
4317           // check for player created from custom element as single target
4318           content = element_info[element].change_page[i].target_element;
4319           is_player = IS_PLAYER_ELEMENT(content);
4320
4321           if (is_player && (found_rating < 3 ||
4322                             (found_rating == 3 && element < found_element)))
4323           {
4324             start_x = x;
4325             start_y = y;
4326
4327             found_rating = 3;
4328             found_element = element;
4329           }
4330         }
4331       }
4332
4333       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4334       {
4335         // check for player created from custom element as explosion content
4336         content = element_info[element].content.e[xx][yy];
4337         is_player = IS_PLAYER_ELEMENT(content);
4338
4339         if (is_player && (found_rating < 2 ||
4340                           (found_rating == 2 && element < found_element)))
4341         {
4342           start_x = x + xx - 1;
4343           start_y = y + yy - 1;
4344
4345           found_rating = 2;
4346           found_element = element;
4347         }
4348
4349         if (!CAN_CHANGE(element))
4350           continue;
4351
4352         for (i = 0; i < element_info[element].num_change_pages; i++)
4353         {
4354           // check for player created from custom element as extended target
4355           content =
4356             element_info[element].change_page[i].target_content.e[xx][yy];
4357
4358           is_player = IS_PLAYER_ELEMENT(content);
4359
4360           if (is_player && (found_rating < 1 ||
4361                             (found_rating == 1 && element < found_element)))
4362           {
4363             start_x = x + xx - 1;
4364             start_y = y + yy - 1;
4365
4366             found_rating = 1;
4367             found_element = element;
4368           }
4369         }
4370       }
4371     }
4372
4373     scroll_x = SCROLL_POSITION_X(start_x);
4374     scroll_y = SCROLL_POSITION_Y(start_y);
4375   }
4376   else
4377   {
4378     scroll_x = SCROLL_POSITION_X(local_player->jx);
4379     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4380   }
4381
4382   // !!! FIX THIS (START) !!!
4383   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4384   {
4385     InitGameEngine_EM();
4386   }
4387   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4388   {
4389     InitGameEngine_SP();
4390   }
4391   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4392   {
4393     InitGameEngine_MM();
4394   }
4395   else
4396   {
4397     DrawLevel(REDRAW_FIELD);
4398     DrawAllPlayers();
4399
4400     // after drawing the level, correct some elements
4401     if (game.timegate_time_left == 0)
4402       CloseAllOpenTimegates();
4403   }
4404
4405   // blit playfield from scroll buffer to normal back buffer for fading in
4406   BlitScreenToBitmap(backbuffer);
4407   // !!! FIX THIS (END) !!!
4408
4409   DrawMaskedBorder(fade_mask);
4410
4411   FadeIn(fade_mask);
4412
4413 #if 1
4414   // full screen redraw is required at this point in the following cases:
4415   // - special editor door undrawn when game was started from level editor
4416   // - drawing area (playfield) was changed and has to be removed completely
4417   redraw_mask = REDRAW_ALL;
4418   BackToFront();
4419 #endif
4420
4421   if (!game.restart_level)
4422   {
4423     // copy default game door content to main double buffer
4424
4425     // !!! CHECK AGAIN !!!
4426     SetPanelBackground();
4427     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4428     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4429   }
4430
4431   SetPanelBackground();
4432   SetDrawBackgroundMask(REDRAW_DOOR_1);
4433
4434   UpdateAndDisplayGameControlValues();
4435
4436   if (!game.restart_level)
4437   {
4438     UnmapGameButtons();
4439     UnmapTapeButtons();
4440
4441     FreeGameButtons();
4442     CreateGameButtons();
4443
4444     MapGameButtons();
4445     MapTapeButtons();
4446
4447     // copy actual game door content to door double buffer for OpenDoor()
4448     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4449
4450     OpenDoor(DOOR_OPEN_ALL);
4451
4452     KeyboardAutoRepeatOffUnlessAutoplay();
4453
4454 #if DEBUG_INIT_PLAYER
4455     DebugPrintPlayerStatus("Player status (final)");
4456 #endif
4457   }
4458
4459   UnmapAllGadgets();
4460
4461   MapGameButtons();
4462   MapTapeButtons();
4463
4464   if (!game.restart_level && !tape.playing)
4465   {
4466     LevelStats_incPlayed(level_nr);
4467
4468     SaveLevelSetup_SeriesInfo();
4469   }
4470
4471   game.restart_level = FALSE;
4472   game.restart_game_message = NULL;
4473
4474   game.request_active = FALSE;
4475   game.request_active_or_moving = FALSE;
4476
4477   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4478     InitGameActions_MM();
4479
4480   SaveEngineSnapshotToListInitial();
4481
4482   if (!game.restart_level)
4483   {
4484     PlaySound(SND_GAME_STARTING);
4485
4486     if (setup.sound_music)
4487       PlayLevelMusic();
4488   }
4489
4490   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4491 }
4492
4493 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4494                         int actual_player_x, int actual_player_y)
4495 {
4496   // this is used for non-R'n'D game engines to update certain engine values
4497
4498   // needed to determine if sounds are played within the visible screen area
4499   scroll_x = actual_scroll_x;
4500   scroll_y = actual_scroll_y;
4501
4502   // needed to get player position for "follow finger" playing input method
4503   local_player->jx = actual_player_x;
4504   local_player->jy = actual_player_y;
4505 }
4506
4507 void InitMovDir(int x, int y)
4508 {
4509   int i, element = Tile[x][y];
4510   static int xy[4][2] =
4511   {
4512     {  0, +1 },
4513     { +1,  0 },
4514     {  0, -1 },
4515     { -1,  0 }
4516   };
4517   static int direction[3][4] =
4518   {
4519     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4520     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4521     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4522   };
4523
4524   switch (element)
4525   {
4526     case EL_BUG_RIGHT:
4527     case EL_BUG_UP:
4528     case EL_BUG_LEFT:
4529     case EL_BUG_DOWN:
4530       Tile[x][y] = EL_BUG;
4531       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4532       break;
4533
4534     case EL_SPACESHIP_RIGHT:
4535     case EL_SPACESHIP_UP:
4536     case EL_SPACESHIP_LEFT:
4537     case EL_SPACESHIP_DOWN:
4538       Tile[x][y] = EL_SPACESHIP;
4539       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4540       break;
4541
4542     case EL_BD_BUTTERFLY_RIGHT:
4543     case EL_BD_BUTTERFLY_UP:
4544     case EL_BD_BUTTERFLY_LEFT:
4545     case EL_BD_BUTTERFLY_DOWN:
4546       Tile[x][y] = EL_BD_BUTTERFLY;
4547       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4548       break;
4549
4550     case EL_BD_FIREFLY_RIGHT:
4551     case EL_BD_FIREFLY_UP:
4552     case EL_BD_FIREFLY_LEFT:
4553     case EL_BD_FIREFLY_DOWN:
4554       Tile[x][y] = EL_BD_FIREFLY;
4555       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4556       break;
4557
4558     case EL_PACMAN_RIGHT:
4559     case EL_PACMAN_UP:
4560     case EL_PACMAN_LEFT:
4561     case EL_PACMAN_DOWN:
4562       Tile[x][y] = EL_PACMAN;
4563       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4564       break;
4565
4566     case EL_YAMYAM_LEFT:
4567     case EL_YAMYAM_RIGHT:
4568     case EL_YAMYAM_UP:
4569     case EL_YAMYAM_DOWN:
4570       Tile[x][y] = EL_YAMYAM;
4571       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4572       break;
4573
4574     case EL_SP_SNIKSNAK:
4575       MovDir[x][y] = MV_UP;
4576       break;
4577
4578     case EL_SP_ELECTRON:
4579       MovDir[x][y] = MV_LEFT;
4580       break;
4581
4582     case EL_MOLE_LEFT:
4583     case EL_MOLE_RIGHT:
4584     case EL_MOLE_UP:
4585     case EL_MOLE_DOWN:
4586       Tile[x][y] = EL_MOLE;
4587       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4588       break;
4589
4590     case EL_SPRING_LEFT:
4591     case EL_SPRING_RIGHT:
4592       Tile[x][y] = EL_SPRING;
4593       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4594       break;
4595
4596     default:
4597       if (IS_CUSTOM_ELEMENT(element))
4598       {
4599         struct ElementInfo *ei = &element_info[element];
4600         int move_direction_initial = ei->move_direction_initial;
4601         int move_pattern = ei->move_pattern;
4602
4603         if (move_direction_initial == MV_START_PREVIOUS)
4604         {
4605           if (MovDir[x][y] != MV_NONE)
4606             return;
4607
4608           move_direction_initial = MV_START_AUTOMATIC;
4609         }
4610
4611         if (move_direction_initial == MV_START_RANDOM)
4612           MovDir[x][y] = 1 << RND(4);
4613         else if (move_direction_initial & MV_ANY_DIRECTION)
4614           MovDir[x][y] = move_direction_initial;
4615         else if (move_pattern == MV_ALL_DIRECTIONS ||
4616                  move_pattern == MV_TURNING_LEFT ||
4617                  move_pattern == MV_TURNING_RIGHT ||
4618                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4619                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4620                  move_pattern == MV_TURNING_RANDOM)
4621           MovDir[x][y] = 1 << RND(4);
4622         else if (move_pattern == MV_HORIZONTAL)
4623           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4624         else if (move_pattern == MV_VERTICAL)
4625           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4626         else if (move_pattern & MV_ANY_DIRECTION)
4627           MovDir[x][y] = element_info[element].move_pattern;
4628         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4629                  move_pattern == MV_ALONG_RIGHT_SIDE)
4630         {
4631           // use random direction as default start direction
4632           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4633             MovDir[x][y] = 1 << RND(4);
4634
4635           for (i = 0; i < NUM_DIRECTIONS; i++)
4636           {
4637             int x1 = x + xy[i][0];
4638             int y1 = y + xy[i][1];
4639
4640             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4641             {
4642               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4643                 MovDir[x][y] = direction[0][i];
4644               else
4645                 MovDir[x][y] = direction[1][i];
4646
4647               break;
4648             }
4649           }
4650         }                
4651       }
4652       else
4653       {
4654         MovDir[x][y] = 1 << RND(4);
4655
4656         if (element != EL_BUG &&
4657             element != EL_SPACESHIP &&
4658             element != EL_BD_BUTTERFLY &&
4659             element != EL_BD_FIREFLY)
4660           break;
4661
4662         for (i = 0; i < NUM_DIRECTIONS; i++)
4663         {
4664           int x1 = x + xy[i][0];
4665           int y1 = y + xy[i][1];
4666
4667           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4668           {
4669             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4670             {
4671               MovDir[x][y] = direction[0][i];
4672               break;
4673             }
4674             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4675                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4676             {
4677               MovDir[x][y] = direction[1][i];
4678               break;
4679             }
4680           }
4681         }
4682       }
4683       break;
4684   }
4685
4686   GfxDir[x][y] = MovDir[x][y];
4687 }
4688
4689 void InitAmoebaNr(int x, int y)
4690 {
4691   int i;
4692   int group_nr = AmoebaNeighbourNr(x, y);
4693
4694   if (group_nr == 0)
4695   {
4696     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4697     {
4698       if (AmoebaCnt[i] == 0)
4699       {
4700         group_nr = i;
4701         break;
4702       }
4703     }
4704   }
4705
4706   AmoebaNr[x][y] = group_nr;
4707   AmoebaCnt[group_nr]++;
4708   AmoebaCnt2[group_nr]++;
4709 }
4710
4711 static void LevelSolved_SetFinalGameValues(void)
4712 {
4713   game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4714   game.score_time_final = (level.use_step_counter ? TimePlayed :
4715                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4716
4717   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4718                       game_em.lev->score :
4719                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4720                       game_mm.score :
4721                       game.score);
4722
4723   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4724                        MM_HEALTH(game_mm.laser_overload_value) :
4725                        game.health);
4726
4727   game.LevelSolved_CountingTime = game.time_final;
4728   game.LevelSolved_CountingScore = game.score_final;
4729   game.LevelSolved_CountingHealth = game.health_final;
4730 }
4731
4732 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4733 {
4734   game.LevelSolved_CountingTime = time;
4735   game.LevelSolved_CountingScore = score;
4736   game.LevelSolved_CountingHealth = health;
4737
4738   game_panel_controls[GAME_PANEL_TIME].value = time;
4739   game_panel_controls[GAME_PANEL_SCORE].value = score;
4740   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4741
4742   DisplayGameControlValues();
4743 }
4744
4745 static void LevelSolved(void)
4746 {
4747   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4748       game.players_still_needed > 0)
4749     return;
4750
4751   game.LevelSolved = TRUE;
4752   game.GameOver = TRUE;
4753
4754   // needed here to display correct panel values while player walks into exit
4755   LevelSolved_SetFinalGameValues();
4756 }
4757
4758 void GameWon(void)
4759 {
4760   static int time_count_steps;
4761   static int time, time_final;
4762   static float score, score_final; // needed for time score < 10 for 10 seconds
4763   static int health, health_final;
4764   static int game_over_delay_1 = 0;
4765   static int game_over_delay_2 = 0;
4766   static int game_over_delay_3 = 0;
4767   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4768   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4769
4770   if (!game.LevelSolved_GameWon)
4771   {
4772     int i;
4773
4774     // do not start end game actions before the player stops moving (to exit)
4775     if (local_player->active && local_player->MovPos)
4776       return;
4777
4778     // calculate final game values after player finished walking into exit
4779     LevelSolved_SetFinalGameValues();
4780
4781     game.LevelSolved_GameWon = TRUE;
4782     game.LevelSolved_SaveTape = tape.recording;
4783     game.LevelSolved_SaveScore = !tape.playing;
4784
4785     if (!tape.playing)
4786     {
4787       LevelStats_incSolved(level_nr);
4788
4789       SaveLevelSetup_SeriesInfo();
4790     }
4791
4792     if (tape.auto_play)         // tape might already be stopped here
4793       tape.auto_play_level_solved = TRUE;
4794
4795     TapeStop();
4796
4797     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4798     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4799     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4800
4801     time = time_final = game.time_final;
4802     score = score_final = game.score_final;
4803     health = health_final = game.health_final;
4804
4805     // update game panel values before (delayed) counting of score (if any)
4806     LevelSolved_DisplayFinalGameValues(time, score, health);
4807
4808     // if level has time score defined, calculate new final game values
4809     if (time_score > 0)
4810     {
4811       int time_final_max = 999;
4812       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4813       int time_frames = 0;
4814       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4815       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4816
4817       if (TimeLeft > 0)
4818       {
4819         time_final = 0;
4820         time_frames = time_frames_left;
4821       }
4822       else if (game.no_time_limit && TimePlayed < time_final_max)
4823       {
4824         time_final = time_final_max;
4825         time_frames = time_frames_final_max - time_frames_played;
4826       }
4827
4828       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4829
4830       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4831
4832       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4833       {
4834         health_final = 0;
4835         score_final += health * time_score;
4836       }
4837
4838       game.score_final = score_final;
4839       game.health_final = health_final;
4840     }
4841
4842     // if not counting score after game, immediately update game panel values
4843     if (level_editor_test_game || !setup.count_score_after_game)
4844     {
4845       time = time_final;
4846       score = score_final;
4847
4848       LevelSolved_DisplayFinalGameValues(time, score, health);
4849     }
4850
4851     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4852     {
4853       // check if last player has left the level
4854       if (game.exit_x >= 0 &&
4855           game.exit_y >= 0)
4856       {
4857         int x = game.exit_x;
4858         int y = game.exit_y;
4859         int element = Tile[x][y];
4860
4861         // close exit door after last player
4862         if ((game.all_players_gone &&
4863              (element == EL_EXIT_OPEN ||
4864               element == EL_SP_EXIT_OPEN ||
4865               element == EL_STEEL_EXIT_OPEN)) ||
4866             element == EL_EM_EXIT_OPEN ||
4867             element == EL_EM_STEEL_EXIT_OPEN)
4868         {
4869
4870           Tile[x][y] =
4871             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4872              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4873              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4874              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4875              EL_EM_STEEL_EXIT_CLOSING);
4876
4877           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4878         }
4879
4880         // player disappears
4881         DrawLevelField(x, y);
4882       }
4883
4884       for (i = 0; i < MAX_PLAYERS; i++)
4885       {
4886         struct PlayerInfo *player = &stored_player[i];
4887
4888         if (player->present)
4889         {
4890           RemovePlayer(player);
4891
4892           // player disappears
4893           DrawLevelField(player->jx, player->jy);
4894         }
4895       }
4896     }
4897
4898     PlaySound(SND_GAME_WINNING);
4899   }
4900
4901   if (setup.count_score_after_game)
4902   {
4903     if (time != time_final)
4904     {
4905       if (game_over_delay_1 > 0)
4906       {
4907         game_over_delay_1--;
4908
4909         return;
4910       }
4911
4912       int time_to_go = ABS(time_final - time);
4913       int time_count_dir = (time < time_final ? +1 : -1);
4914
4915       if (time_to_go < time_count_steps)
4916         time_count_steps = 1;
4917
4918       time  += time_count_steps * time_count_dir;
4919       score += time_count_steps * time_score;
4920
4921       // set final score to correct rounding differences after counting score
4922       if (time == time_final)
4923         score = score_final;
4924
4925       LevelSolved_DisplayFinalGameValues(time, score, health);
4926
4927       if (time == time_final)
4928         StopSound(SND_GAME_LEVELTIME_BONUS);
4929       else if (setup.sound_loops)
4930         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4931       else
4932         PlaySound(SND_GAME_LEVELTIME_BONUS);
4933
4934       return;
4935     }
4936
4937     if (health != health_final)
4938     {
4939       if (game_over_delay_2 > 0)
4940       {
4941         game_over_delay_2--;
4942
4943         return;
4944       }
4945
4946       int health_count_dir = (health < health_final ? +1 : -1);
4947
4948       health += health_count_dir;
4949       score  += time_score;
4950
4951       LevelSolved_DisplayFinalGameValues(time, score, health);
4952
4953       if (health == health_final)
4954         StopSound(SND_GAME_LEVELTIME_BONUS);
4955       else if (setup.sound_loops)
4956         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4957       else
4958         PlaySound(SND_GAME_LEVELTIME_BONUS);
4959
4960       return;
4961     }
4962   }
4963
4964   game.panel.active = FALSE;
4965
4966   if (game_over_delay_3 > 0)
4967   {
4968     game_over_delay_3--;
4969
4970     return;
4971   }
4972
4973   GameEnd();
4974 }
4975
4976 void GameEnd(void)
4977 {
4978   // used instead of "level_nr" (needed for network games)
4979   int last_level_nr = levelset.level_nr;
4980   boolean tape_saved = FALSE;
4981
4982   game.LevelSolved_GameEnd = TRUE;
4983
4984   if (game.LevelSolved_SaveTape)
4985   {
4986     // make sure that request dialog to save tape does not open door again
4987     if (!global.use_envelope_request)
4988       CloseDoor(DOOR_CLOSE_1);
4989
4990     // ask to save tape
4991     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
4992
4993     // set unique basename for score tape (also saved in high score table)
4994     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
4995   }
4996
4997   // if no tape is to be saved, close both doors simultaneously
4998   CloseDoor(DOOR_CLOSE_ALL);
4999
5000   if (level_editor_test_game)
5001   {
5002     SetGameStatus(GAME_MODE_MAIN);
5003
5004     DrawMainMenu();
5005
5006     return;
5007   }
5008
5009   if (!game.LevelSolved_SaveScore)
5010   {
5011     SetGameStatus(GAME_MODE_MAIN);
5012
5013     DrawMainMenu();
5014
5015     return;
5016   }
5017
5018   if (level_nr == leveldir_current->handicap_level)
5019   {
5020     leveldir_current->handicap_level++;
5021
5022     SaveLevelSetup_SeriesInfo();
5023   }
5024
5025   // save score and score tape before potentially erasing tape below
5026   NewHighScore(last_level_nr, tape_saved);
5027
5028   if (setup.increment_levels &&
5029       level_nr < leveldir_current->last_level &&
5030       !network_playing)
5031   {
5032     level_nr++;         // advance to next level
5033     TapeErase();        // start with empty tape
5034
5035     if (setup.auto_play_next_level)
5036     {
5037       LoadLevel(level_nr);
5038
5039       SaveLevelSetup_SeriesInfo();
5040     }
5041   }
5042
5043   if (scores.last_added >= 0 && setup.show_scores_after_game)
5044   {
5045     SetGameStatus(GAME_MODE_SCORES);
5046
5047     DrawHallOfFame(last_level_nr);
5048   }
5049   else if (setup.auto_play_next_level && setup.increment_levels &&
5050            last_level_nr < leveldir_current->last_level &&
5051            !network_playing)
5052   {
5053     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5054   }
5055   else
5056   {
5057     SetGameStatus(GAME_MODE_MAIN);
5058
5059     DrawMainMenu();
5060   }
5061 }
5062
5063 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5064                          boolean one_score_entry_per_name)
5065 {
5066   int i;
5067
5068   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5069     return -1;
5070
5071   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5072   {
5073     struct ScoreEntry *entry = &list->entry[i];
5074     boolean score_is_better = (new_entry->score >  entry->score);
5075     boolean score_is_equal  = (new_entry->score == entry->score);
5076     boolean time_is_better  = (new_entry->time  <  entry->time);
5077     boolean time_is_equal   = (new_entry->time  == entry->time);
5078     boolean better_by_score = (score_is_better ||
5079                                (score_is_equal && time_is_better));
5080     boolean better_by_time  = (time_is_better ||
5081                                (time_is_equal && score_is_better));
5082     boolean is_better = (level.rate_time_over_score ? better_by_time :
5083                          better_by_score);
5084     boolean entry_is_empty = (entry->score == 0 &&
5085                               entry->time == 0);
5086
5087     // prevent adding server score entries if also existing in local score file
5088     // (special case: historic score entries have an empty tape basename entry)
5089     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5090         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5091       return -1;
5092
5093     if (is_better || entry_is_empty)
5094     {
5095       // player has made it to the hall of fame
5096
5097       if (i < MAX_SCORE_ENTRIES - 1)
5098       {
5099         int m = MAX_SCORE_ENTRIES - 1;
5100         int l;
5101
5102         if (one_score_entry_per_name)
5103         {
5104           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5105             if (strEqual(list->entry[l].name, new_entry->name))
5106               m = l;
5107
5108           if (m == i)   // player's new highscore overwrites his old one
5109             goto put_into_list;
5110         }
5111
5112         for (l = m; l > i; l--)
5113           list->entry[l] = list->entry[l - 1];
5114       }
5115
5116       put_into_list:
5117
5118       *entry = *new_entry;
5119
5120       return i;
5121     }
5122     else if (one_score_entry_per_name &&
5123              strEqual(entry->name, new_entry->name))
5124     {
5125       // player already in high score list with better score or time
5126
5127       return -1;
5128     }
5129   }
5130
5131   return -1;
5132 }
5133
5134 void NewHighScore(int level_nr, boolean tape_saved)
5135 {
5136   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5137   boolean one_per_name = FALSE;
5138
5139   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5140   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5141
5142   new_entry.score = game.score_final;
5143   new_entry.time = game.score_time_final;
5144
5145   LoadScore(level_nr);
5146
5147   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5148
5149   if (scores.last_added < 0)
5150     return;
5151
5152   SaveScore(level_nr);
5153
5154   // store last added local score entry (before merging server scores)
5155   scores.last_added_local = scores.last_added;
5156
5157   if (!game.LevelSolved_SaveTape)
5158     return;
5159
5160   SaveScoreTape(level_nr);
5161
5162   if (setup.ask_for_using_api_server)
5163   {
5164     setup.use_api_server =
5165       Request("Upload your score and tape to the high score server?", REQ_ASK);
5166
5167     if (!setup.use_api_server)
5168       Request("Not using high score server! Use setup menu to enable again!",
5169               REQ_CONFIRM);
5170
5171     runtime.use_api_server = setup.use_api_server;
5172
5173     // after asking for using API server once, do not ask again
5174     setup.ask_for_using_api_server = FALSE;
5175
5176     SaveSetup_ServerSetup();
5177   }
5178
5179   SaveServerScore(level_nr, tape_saved);
5180 }
5181
5182 void MergeServerScore(void)
5183 {
5184   struct ScoreEntry last_added_entry;
5185   boolean one_per_name = FALSE;
5186   int i;
5187
5188   if (scores.last_added >= 0)
5189     last_added_entry = scores.entry[scores.last_added];
5190
5191   for (i = 0; i < server_scores.num_entries; i++)
5192   {
5193     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5194
5195     if (pos >= 0 && pos <= scores.last_added)
5196       scores.last_added++;
5197   }
5198
5199   if (scores.last_added >= MAX_SCORE_ENTRIES)
5200   {
5201     scores.last_added = MAX_SCORE_ENTRIES - 1;
5202     scores.force_last_added = TRUE;
5203
5204     scores.entry[scores.last_added] = last_added_entry;
5205   }
5206 }
5207
5208 static int getElementMoveStepsizeExt(int x, int y, int direction)
5209 {
5210   int element = Tile[x][y];
5211   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5212   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5213   int horiz_move = (dx != 0);
5214   int sign = (horiz_move ? dx : dy);
5215   int step = sign * element_info[element].move_stepsize;
5216
5217   // special values for move stepsize for spring and things on conveyor belt
5218   if (horiz_move)
5219   {
5220     if (CAN_FALL(element) &&
5221         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5222       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5223     else if (element == EL_SPRING)
5224       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5225   }
5226
5227   return step;
5228 }
5229
5230 static int getElementMoveStepsize(int x, int y)
5231 {
5232   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5233 }
5234
5235 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5236 {
5237   if (player->GfxAction != action || player->GfxDir != dir)
5238   {
5239     player->GfxAction = action;
5240     player->GfxDir = dir;
5241     player->Frame = 0;
5242     player->StepFrame = 0;
5243   }
5244 }
5245
5246 static void ResetGfxFrame(int x, int y)
5247 {
5248   // profiling showed that "autotest" spends 10~20% of its time in this function
5249   if (DrawingDeactivatedField())
5250     return;
5251
5252   int element = Tile[x][y];
5253   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5254
5255   if (graphic_info[graphic].anim_global_sync)
5256     GfxFrame[x][y] = FrameCounter;
5257   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5258     GfxFrame[x][y] = CustomValue[x][y];
5259   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5260     GfxFrame[x][y] = element_info[element].collect_score;
5261   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5262     GfxFrame[x][y] = ChangeDelay[x][y];
5263 }
5264
5265 static void ResetGfxAnimation(int x, int y)
5266 {
5267   GfxAction[x][y] = ACTION_DEFAULT;
5268   GfxDir[x][y] = MovDir[x][y];
5269   GfxFrame[x][y] = 0;
5270
5271   ResetGfxFrame(x, y);
5272 }
5273
5274 static void ResetRandomAnimationValue(int x, int y)
5275 {
5276   GfxRandom[x][y] = INIT_GFX_RANDOM();
5277 }
5278
5279 static void InitMovingField(int x, int y, int direction)
5280 {
5281   int element = Tile[x][y];
5282   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5283   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5284   int newx = x + dx;
5285   int newy = y + dy;
5286   boolean is_moving_before, is_moving_after;
5287
5288   // check if element was/is moving or being moved before/after mode change
5289   is_moving_before = (WasJustMoving[x][y] != 0);
5290   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5291
5292   // reset animation only for moving elements which change direction of moving
5293   // or which just started or stopped moving
5294   // (else CEs with property "can move" / "not moving" are reset each frame)
5295   if (is_moving_before != is_moving_after ||
5296       direction != MovDir[x][y])
5297     ResetGfxAnimation(x, y);
5298
5299   MovDir[x][y] = direction;
5300   GfxDir[x][y] = direction;
5301
5302   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5303                      direction == MV_DOWN && CAN_FALL(element) ?
5304                      ACTION_FALLING : ACTION_MOVING);
5305
5306   // this is needed for CEs with property "can move" / "not moving"
5307
5308   if (is_moving_after)
5309   {
5310     if (Tile[newx][newy] == EL_EMPTY)
5311       Tile[newx][newy] = EL_BLOCKED;
5312
5313     MovDir[newx][newy] = MovDir[x][y];
5314
5315     CustomValue[newx][newy] = CustomValue[x][y];
5316
5317     GfxFrame[newx][newy] = GfxFrame[x][y];
5318     GfxRandom[newx][newy] = GfxRandom[x][y];
5319     GfxAction[newx][newy] = GfxAction[x][y];
5320     GfxDir[newx][newy] = GfxDir[x][y];
5321   }
5322 }
5323
5324 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5325 {
5326   int direction = MovDir[x][y];
5327   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5328   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5329
5330   *goes_to_x = newx;
5331   *goes_to_y = newy;
5332 }
5333
5334 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5335 {
5336   int oldx = x, oldy = y;
5337   int direction = MovDir[x][y];
5338
5339   if (direction == MV_LEFT)
5340     oldx++;
5341   else if (direction == MV_RIGHT)
5342     oldx--;
5343   else if (direction == MV_UP)
5344     oldy++;
5345   else if (direction == MV_DOWN)
5346     oldy--;
5347
5348   *comes_from_x = oldx;
5349   *comes_from_y = oldy;
5350 }
5351
5352 static int MovingOrBlocked2Element(int x, int y)
5353 {
5354   int element = Tile[x][y];
5355
5356   if (element == EL_BLOCKED)
5357   {
5358     int oldx, oldy;
5359
5360     Blocked2Moving(x, y, &oldx, &oldy);
5361     return Tile[oldx][oldy];
5362   }
5363   else
5364     return element;
5365 }
5366
5367 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5368 {
5369   // like MovingOrBlocked2Element(), but if element is moving
5370   // and (x,y) is the field the moving element is just leaving,
5371   // return EL_BLOCKED instead of the element value
5372   int element = Tile[x][y];
5373
5374   if (IS_MOVING(x, y))
5375   {
5376     if (element == EL_BLOCKED)
5377     {
5378       int oldx, oldy;
5379
5380       Blocked2Moving(x, y, &oldx, &oldy);
5381       return Tile[oldx][oldy];
5382     }
5383     else
5384       return EL_BLOCKED;
5385   }
5386   else
5387     return element;
5388 }
5389
5390 static void RemoveField(int x, int y)
5391 {
5392   Tile[x][y] = EL_EMPTY;
5393
5394   MovPos[x][y] = 0;
5395   MovDir[x][y] = 0;
5396   MovDelay[x][y] = 0;
5397
5398   CustomValue[x][y] = 0;
5399
5400   AmoebaNr[x][y] = 0;
5401   ChangeDelay[x][y] = 0;
5402   ChangePage[x][y] = -1;
5403   Pushed[x][y] = FALSE;
5404
5405   GfxElement[x][y] = EL_UNDEFINED;
5406   GfxAction[x][y] = ACTION_DEFAULT;
5407   GfxDir[x][y] = MV_NONE;
5408 }
5409
5410 static void RemoveMovingField(int x, int y)
5411 {
5412   int oldx = x, oldy = y, newx = x, newy = y;
5413   int element = Tile[x][y];
5414   int next_element = EL_UNDEFINED;
5415
5416   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5417     return;
5418
5419   if (IS_MOVING(x, y))
5420   {
5421     Moving2Blocked(x, y, &newx, &newy);
5422
5423     if (Tile[newx][newy] != EL_BLOCKED)
5424     {
5425       // element is moving, but target field is not free (blocked), but
5426       // already occupied by something different (example: acid pool);
5427       // in this case, only remove the moving field, but not the target
5428
5429       RemoveField(oldx, oldy);
5430
5431       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5432
5433       TEST_DrawLevelField(oldx, oldy);
5434
5435       return;
5436     }
5437   }
5438   else if (element == EL_BLOCKED)
5439   {
5440     Blocked2Moving(x, y, &oldx, &oldy);
5441     if (!IS_MOVING(oldx, oldy))
5442       return;
5443   }
5444
5445   if (element == EL_BLOCKED &&
5446       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5447        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5448        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5449        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5450        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5451        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5452     next_element = get_next_element(Tile[oldx][oldy]);
5453
5454   RemoveField(oldx, oldy);
5455   RemoveField(newx, newy);
5456
5457   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5458
5459   if (next_element != EL_UNDEFINED)
5460     Tile[oldx][oldy] = next_element;
5461
5462   TEST_DrawLevelField(oldx, oldy);
5463   TEST_DrawLevelField(newx, newy);
5464 }
5465
5466 void DrawDynamite(int x, int y)
5467 {
5468   int sx = SCREENX(x), sy = SCREENY(y);
5469   int graphic = el2img(Tile[x][y]);
5470   int frame;
5471
5472   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5473     return;
5474
5475   if (IS_WALKABLE_INSIDE(Back[x][y]))
5476     return;
5477
5478   if (Back[x][y])
5479     DrawLevelElement(x, y, Back[x][y]);
5480   else if (Store[x][y])
5481     DrawLevelElement(x, y, Store[x][y]);
5482   else if (game.use_masked_elements)
5483     DrawLevelElement(x, y, EL_EMPTY);
5484
5485   frame = getGraphicAnimationFrameXY(graphic, x, y);
5486
5487   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5488     DrawGraphicThruMask(sx, sy, graphic, frame);
5489   else
5490     DrawGraphic(sx, sy, graphic, frame);
5491 }
5492
5493 static void CheckDynamite(int x, int y)
5494 {
5495   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5496   {
5497     MovDelay[x][y]--;
5498
5499     if (MovDelay[x][y] != 0)
5500     {
5501       DrawDynamite(x, y);
5502       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5503
5504       return;
5505     }
5506   }
5507
5508   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5509
5510   Bang(x, y);
5511 }
5512
5513 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5514 {
5515   boolean num_checked_players = 0;
5516   int i;
5517
5518   for (i = 0; i < MAX_PLAYERS; i++)
5519   {
5520     if (stored_player[i].active)
5521     {
5522       int sx = stored_player[i].jx;
5523       int sy = stored_player[i].jy;
5524
5525       if (num_checked_players == 0)
5526       {
5527         *sx1 = *sx2 = sx;
5528         *sy1 = *sy2 = sy;
5529       }
5530       else
5531       {
5532         *sx1 = MIN(*sx1, sx);
5533         *sy1 = MIN(*sy1, sy);
5534         *sx2 = MAX(*sx2, sx);
5535         *sy2 = MAX(*sy2, sy);
5536       }
5537
5538       num_checked_players++;
5539     }
5540   }
5541 }
5542
5543 static boolean checkIfAllPlayersFitToScreen_RND(void)
5544 {
5545   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5546
5547   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5548
5549   return (sx2 - sx1 < SCR_FIELDX &&
5550           sy2 - sy1 < SCR_FIELDY);
5551 }
5552
5553 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5554 {
5555   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5556
5557   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5558
5559   *sx = (sx1 + sx2) / 2;
5560   *sy = (sy1 + sy2) / 2;
5561 }
5562
5563 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5564                                boolean center_screen, boolean quick_relocation)
5565 {
5566   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5567   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5568   boolean no_delay = (tape.warp_forward);
5569   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5570   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5571   int new_scroll_x, new_scroll_y;
5572
5573   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5574   {
5575     // case 1: quick relocation inside visible screen (without scrolling)
5576
5577     RedrawPlayfield();
5578
5579     return;
5580   }
5581
5582   if (!level.shifted_relocation || center_screen)
5583   {
5584     // relocation _with_ centering of screen
5585
5586     new_scroll_x = SCROLL_POSITION_X(x);
5587     new_scroll_y = SCROLL_POSITION_Y(y);
5588   }
5589   else
5590   {
5591     // relocation _without_ centering of screen
5592
5593     int center_scroll_x = SCROLL_POSITION_X(old_x);
5594     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5595     int offset_x = x + (scroll_x - center_scroll_x);
5596     int offset_y = y + (scroll_y - center_scroll_y);
5597
5598     // for new screen position, apply previous offset to center position
5599     new_scroll_x = SCROLL_POSITION_X(offset_x);
5600     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5601   }
5602
5603   if (quick_relocation)
5604   {
5605     // case 2: quick relocation (redraw without visible scrolling)
5606
5607     scroll_x = new_scroll_x;
5608     scroll_y = new_scroll_y;
5609
5610     RedrawPlayfield();
5611
5612     return;
5613   }
5614
5615   // case 3: visible relocation (with scrolling to new position)
5616
5617   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5618
5619   SetVideoFrameDelay(wait_delay_value);
5620
5621   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5622   {
5623     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5624     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5625
5626     if (dx == 0 && dy == 0)             // no scrolling needed at all
5627       break;
5628
5629     scroll_x -= dx;
5630     scroll_y -= dy;
5631
5632     // set values for horizontal/vertical screen scrolling (half tile size)
5633     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5634     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5635     int pos_x = dx * TILEX / 2;
5636     int pos_y = dy * TILEY / 2;
5637     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5638     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5639
5640     ScrollLevel(dx, dy);
5641     DrawAllPlayers();
5642
5643     // scroll in two steps of half tile size to make things smoother
5644     BlitScreenToBitmapExt_RND(window, fx, fy);
5645
5646     // scroll second step to align at full tile size
5647     BlitScreenToBitmap(window);
5648   }
5649
5650   DrawAllPlayers();
5651   BackToFront();
5652
5653   SetVideoFrameDelay(frame_delay_value_old);
5654 }
5655
5656 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5657 {
5658   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5659   int player_nr = GET_PLAYER_NR(el_player);
5660   struct PlayerInfo *player = &stored_player[player_nr];
5661   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5662   boolean no_delay = (tape.warp_forward);
5663   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5664   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5665   int old_jx = player->jx;
5666   int old_jy = player->jy;
5667   int old_element = Tile[old_jx][old_jy];
5668   int element = Tile[jx][jy];
5669   boolean player_relocated = (old_jx != jx || old_jy != jy);
5670
5671   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5672   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5673   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5674   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5675   int leave_side_horiz = move_dir_horiz;
5676   int leave_side_vert  = move_dir_vert;
5677   int enter_side = enter_side_horiz | enter_side_vert;
5678   int leave_side = leave_side_horiz | leave_side_vert;
5679
5680   if (player->buried)           // do not reanimate dead player
5681     return;
5682
5683   if (!player_relocated)        // no need to relocate the player
5684     return;
5685
5686   if (IS_PLAYER(jx, jy))        // player already placed at new position
5687   {
5688     RemoveField(jx, jy);        // temporarily remove newly placed player
5689     DrawLevelField(jx, jy);
5690   }
5691
5692   if (player->present)
5693   {
5694     while (player->MovPos)
5695     {
5696       ScrollPlayer(player, SCROLL_GO_ON);
5697       ScrollScreen(NULL, SCROLL_GO_ON);
5698
5699       AdvanceFrameAndPlayerCounters(player->index_nr);
5700
5701       DrawPlayer(player);
5702
5703       BackToFront_WithFrameDelay(wait_delay_value);
5704     }
5705
5706     DrawPlayer(player);         // needed here only to cleanup last field
5707     DrawLevelField(player->jx, player->jy);     // remove player graphic
5708
5709     player->is_moving = FALSE;
5710   }
5711
5712   if (IS_CUSTOM_ELEMENT(old_element))
5713     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5714                                CE_LEFT_BY_PLAYER,
5715                                player->index_bit, leave_side);
5716
5717   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5718                                       CE_PLAYER_LEAVES_X,
5719                                       player->index_bit, leave_side);
5720
5721   Tile[jx][jy] = el_player;
5722   InitPlayerField(jx, jy, el_player, TRUE);
5723
5724   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5725      possible that the relocation target field did not contain a player element,
5726      but a walkable element, to which the new player was relocated -- in this
5727      case, restore that (already initialized!) element on the player field */
5728   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5729   {
5730     Tile[jx][jy] = element;     // restore previously existing element
5731   }
5732
5733   // only visually relocate centered player
5734   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5735                      FALSE, level.instant_relocation);
5736
5737   TestIfPlayerTouchesBadThing(jx, jy);
5738   TestIfPlayerTouchesCustomElement(jx, jy);
5739
5740   if (IS_CUSTOM_ELEMENT(element))
5741     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5742                                player->index_bit, enter_side);
5743
5744   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5745                                       player->index_bit, enter_side);
5746
5747   if (player->is_switching)
5748   {
5749     /* ensure that relocation while still switching an element does not cause
5750        a new element to be treated as also switched directly after relocation
5751        (this is important for teleporter switches that teleport the player to
5752        a place where another teleporter switch is in the same direction, which
5753        would then incorrectly be treated as immediately switched before the
5754        direction key that caused the switch was released) */
5755
5756     player->switch_x += jx - old_jx;
5757     player->switch_y += jy - old_jy;
5758   }
5759 }
5760
5761 static void Explode(int ex, int ey, int phase, int mode)
5762 {
5763   int x, y;
5764   int last_phase;
5765   int border_element;
5766
5767   // !!! eliminate this variable !!!
5768   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5769
5770   if (game.explosions_delayed)
5771   {
5772     ExplodeField[ex][ey] = mode;
5773     return;
5774   }
5775
5776   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5777   {
5778     int center_element = Tile[ex][ey];
5779     int artwork_element, explosion_element;     // set these values later
5780
5781     // remove things displayed in background while burning dynamite
5782     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5783       Back[ex][ey] = 0;
5784
5785     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5786     {
5787       // put moving element to center field (and let it explode there)
5788       center_element = MovingOrBlocked2Element(ex, ey);
5789       RemoveMovingField(ex, ey);
5790       Tile[ex][ey] = center_element;
5791     }
5792
5793     // now "center_element" is finally determined -- set related values now
5794     artwork_element = center_element;           // for custom player artwork
5795     explosion_element = center_element;         // for custom player artwork
5796
5797     if (IS_PLAYER(ex, ey))
5798     {
5799       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5800
5801       artwork_element = stored_player[player_nr].artwork_element;
5802
5803       if (level.use_explosion_element[player_nr])
5804       {
5805         explosion_element = level.explosion_element[player_nr];
5806         artwork_element = explosion_element;
5807       }
5808     }
5809
5810     if (mode == EX_TYPE_NORMAL ||
5811         mode == EX_TYPE_CENTER ||
5812         mode == EX_TYPE_CROSS)
5813       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5814
5815     last_phase = element_info[explosion_element].explosion_delay + 1;
5816
5817     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5818     {
5819       int xx = x - ex + 1;
5820       int yy = y - ey + 1;
5821       int element;
5822
5823       if (!IN_LEV_FIELD(x, y) ||
5824           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5825           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5826         continue;
5827
5828       element = Tile[x][y];
5829
5830       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5831       {
5832         element = MovingOrBlocked2Element(x, y);
5833
5834         if (!IS_EXPLOSION_PROOF(element))
5835           RemoveMovingField(x, y);
5836       }
5837
5838       // indestructible elements can only explode in center (but not flames)
5839       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5840                                            mode == EX_TYPE_BORDER)) ||
5841           element == EL_FLAMES)
5842         continue;
5843
5844       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5845          behaviour, for example when touching a yamyam that explodes to rocks
5846          with active deadly shield, a rock is created under the player !!! */
5847       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5848 #if 0
5849       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5850           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5851            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5852 #else
5853       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5854 #endif
5855       {
5856         if (IS_ACTIVE_BOMB(element))
5857         {
5858           // re-activate things under the bomb like gate or penguin
5859           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5860           Back[x][y] = 0;
5861         }
5862
5863         continue;
5864       }
5865
5866       // save walkable background elements while explosion on same tile
5867       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5868           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5869         Back[x][y] = element;
5870
5871       // ignite explodable elements reached by other explosion
5872       if (element == EL_EXPLOSION)
5873         element = Store2[x][y];
5874
5875       if (AmoebaNr[x][y] &&
5876           (element == EL_AMOEBA_FULL ||
5877            element == EL_BD_AMOEBA ||
5878            element == EL_AMOEBA_GROWING))
5879       {
5880         AmoebaCnt[AmoebaNr[x][y]]--;
5881         AmoebaCnt2[AmoebaNr[x][y]]--;
5882       }
5883
5884       RemoveField(x, y);
5885
5886       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5887       {
5888         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5889
5890         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5891
5892         if (PLAYERINFO(ex, ey)->use_murphy)
5893           Store[x][y] = EL_EMPTY;
5894       }
5895
5896       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5897       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5898       else if (IS_PLAYER_ELEMENT(center_element))
5899         Store[x][y] = EL_EMPTY;
5900       else if (center_element == EL_YAMYAM)
5901         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5902       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5903         Store[x][y] = element_info[center_element].content.e[xx][yy];
5904 #if 1
5905       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5906       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5907       // otherwise) -- FIX THIS !!!
5908       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5909         Store[x][y] = element_info[element].content.e[1][1];
5910 #else
5911       else if (!CAN_EXPLODE(element))
5912         Store[x][y] = element_info[element].content.e[1][1];
5913 #endif
5914       else
5915         Store[x][y] = EL_EMPTY;
5916
5917       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5918           center_element == EL_AMOEBA_TO_DIAMOND)
5919         Store2[x][y] = element;
5920
5921       Tile[x][y] = EL_EXPLOSION;
5922       GfxElement[x][y] = artwork_element;
5923
5924       ExplodePhase[x][y] = 1;
5925       ExplodeDelay[x][y] = last_phase;
5926
5927       Stop[x][y] = TRUE;
5928     }
5929
5930     if (center_element == EL_YAMYAM)
5931       game.yamyam_content_nr =
5932         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5933
5934     return;
5935   }
5936
5937   if (Stop[ex][ey])
5938     return;
5939
5940   x = ex;
5941   y = ey;
5942
5943   if (phase == 1)
5944     GfxFrame[x][y] = 0;         // restart explosion animation
5945
5946   last_phase = ExplodeDelay[x][y];
5947
5948   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5949
5950   // this can happen if the player leaves an explosion just in time
5951   if (GfxElement[x][y] == EL_UNDEFINED)
5952     GfxElement[x][y] = EL_EMPTY;
5953
5954   border_element = Store2[x][y];
5955   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5956     border_element = StorePlayer[x][y];
5957
5958   if (phase == element_info[border_element].ignition_delay ||
5959       phase == last_phase)
5960   {
5961     boolean border_explosion = FALSE;
5962
5963     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5964         !PLAYER_EXPLOSION_PROTECTED(x, y))
5965     {
5966       KillPlayerUnlessExplosionProtected(x, y);
5967       border_explosion = TRUE;
5968     }
5969     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5970     {
5971       Tile[x][y] = Store2[x][y];
5972       Store2[x][y] = 0;
5973       Bang(x, y);
5974       border_explosion = TRUE;
5975     }
5976     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5977     {
5978       AmoebaToDiamond(x, y);
5979       Store2[x][y] = 0;
5980       border_explosion = TRUE;
5981     }
5982
5983     // if an element just explodes due to another explosion (chain-reaction),
5984     // do not immediately end the new explosion when it was the last frame of
5985     // the explosion (as it would be done in the following "if"-statement!)
5986     if (border_explosion && phase == last_phase)
5987       return;
5988   }
5989
5990   if (phase == last_phase)
5991   {
5992     int element;
5993
5994     element = Tile[x][y] = Store[x][y];
5995     Store[x][y] = Store2[x][y] = 0;
5996     GfxElement[x][y] = EL_UNDEFINED;
5997
5998     // player can escape from explosions and might therefore be still alive
5999     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6000         element <= EL_PLAYER_IS_EXPLODING_4)
6001     {
6002       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6003       int explosion_element = EL_PLAYER_1 + player_nr;
6004       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6005       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6006
6007       if (level.use_explosion_element[player_nr])
6008         explosion_element = level.explosion_element[player_nr];
6009
6010       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6011                     element_info[explosion_element].content.e[xx][yy]);
6012     }
6013
6014     // restore probably existing indestructible background element
6015     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6016       element = Tile[x][y] = Back[x][y];
6017     Back[x][y] = 0;
6018
6019     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6020     GfxDir[x][y] = MV_NONE;
6021     ChangeDelay[x][y] = 0;
6022     ChangePage[x][y] = -1;
6023
6024     CustomValue[x][y] = 0;
6025
6026     InitField_WithBug2(x, y, FALSE);
6027
6028     TEST_DrawLevelField(x, y);
6029
6030     TestIfElementTouchesCustomElement(x, y);
6031
6032     if (GFX_CRUMBLED(element))
6033       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6034
6035     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6036       StorePlayer[x][y] = 0;
6037
6038     if (IS_PLAYER_ELEMENT(element))
6039       RelocatePlayer(x, y, element);
6040   }
6041   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6042   {
6043     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6044     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6045
6046     if (phase == delay)
6047       TEST_DrawLevelFieldCrumbled(x, y);
6048
6049     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6050     {
6051       DrawLevelElement(x, y, Back[x][y]);
6052       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6053     }
6054     else if (IS_WALKABLE_UNDER(Back[x][y]))
6055     {
6056       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6057       DrawLevelElementThruMask(x, y, Back[x][y]);
6058     }
6059     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6060       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6061   }
6062 }
6063
6064 static void DynaExplode(int ex, int ey)
6065 {
6066   int i, j;
6067   int dynabomb_element = Tile[ex][ey];
6068   int dynabomb_size = 1;
6069   boolean dynabomb_xl = FALSE;
6070   struct PlayerInfo *player;
6071   static int xy[4][2] =
6072   {
6073     { 0, -1 },
6074     { -1, 0 },
6075     { +1, 0 },
6076     { 0, +1 }
6077   };
6078
6079   if (IS_ACTIVE_BOMB(dynabomb_element))
6080   {
6081     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6082     dynabomb_size = player->dynabomb_size;
6083     dynabomb_xl = player->dynabomb_xl;
6084     player->dynabombs_left++;
6085   }
6086
6087   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6088
6089   for (i = 0; i < NUM_DIRECTIONS; i++)
6090   {
6091     for (j = 1; j <= dynabomb_size; j++)
6092     {
6093       int x = ex + j * xy[i][0];
6094       int y = ey + j * xy[i][1];
6095       int element;
6096
6097       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6098         break;
6099
6100       element = Tile[x][y];
6101
6102       // do not restart explosions of fields with active bombs
6103       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6104         continue;
6105
6106       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6107
6108       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6109           !IS_DIGGABLE(element) && !dynabomb_xl)
6110         break;
6111     }
6112   }
6113 }
6114
6115 void Bang(int x, int y)
6116 {
6117   int element = MovingOrBlocked2Element(x, y);
6118   int explosion_type = EX_TYPE_NORMAL;
6119
6120   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6121   {
6122     struct PlayerInfo *player = PLAYERINFO(x, y);
6123
6124     element = Tile[x][y] = player->initial_element;
6125
6126     if (level.use_explosion_element[player->index_nr])
6127     {
6128       int explosion_element = level.explosion_element[player->index_nr];
6129
6130       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6131         explosion_type = EX_TYPE_CROSS;
6132       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6133         explosion_type = EX_TYPE_CENTER;
6134     }
6135   }
6136
6137   switch (element)
6138   {
6139     case EL_BUG:
6140     case EL_SPACESHIP:
6141     case EL_BD_BUTTERFLY:
6142     case EL_BD_FIREFLY:
6143     case EL_YAMYAM:
6144     case EL_DARK_YAMYAM:
6145     case EL_ROBOT:
6146     case EL_PACMAN:
6147     case EL_MOLE:
6148       RaiseScoreElement(element);
6149       break;
6150
6151     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6152     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6153     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6154     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6155     case EL_DYNABOMB_INCREASE_NUMBER:
6156     case EL_DYNABOMB_INCREASE_SIZE:
6157     case EL_DYNABOMB_INCREASE_POWER:
6158       explosion_type = EX_TYPE_DYNA;
6159       break;
6160
6161     case EL_DC_LANDMINE:
6162       explosion_type = EX_TYPE_CENTER;
6163       break;
6164
6165     case EL_PENGUIN:
6166     case EL_LAMP:
6167     case EL_LAMP_ACTIVE:
6168     case EL_AMOEBA_TO_DIAMOND:
6169       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6170         explosion_type = EX_TYPE_CENTER;
6171       break;
6172
6173     default:
6174       if (element_info[element].explosion_type == EXPLODES_CROSS)
6175         explosion_type = EX_TYPE_CROSS;
6176       else if (element_info[element].explosion_type == EXPLODES_1X1)
6177         explosion_type = EX_TYPE_CENTER;
6178       break;
6179   }
6180
6181   if (explosion_type == EX_TYPE_DYNA)
6182     DynaExplode(x, y);
6183   else
6184     Explode(x, y, EX_PHASE_START, explosion_type);
6185
6186   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6187 }
6188
6189 static void SplashAcid(int x, int y)
6190 {
6191   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6192       (!IN_LEV_FIELD(x - 1, y - 2) ||
6193        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6194     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6195
6196   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6197       (!IN_LEV_FIELD(x + 1, y - 2) ||
6198        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6199     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6200
6201   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6202 }
6203
6204 static void InitBeltMovement(void)
6205 {
6206   static int belt_base_element[4] =
6207   {
6208     EL_CONVEYOR_BELT_1_LEFT,
6209     EL_CONVEYOR_BELT_2_LEFT,
6210     EL_CONVEYOR_BELT_3_LEFT,
6211     EL_CONVEYOR_BELT_4_LEFT
6212   };
6213   static int belt_base_active_element[4] =
6214   {
6215     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6216     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6217     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6218     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6219   };
6220
6221   int x, y, i, j;
6222
6223   // set frame order for belt animation graphic according to belt direction
6224   for (i = 0; i < NUM_BELTS; i++)
6225   {
6226     int belt_nr = i;
6227
6228     for (j = 0; j < NUM_BELT_PARTS; j++)
6229     {
6230       int element = belt_base_active_element[belt_nr] + j;
6231       int graphic_1 = el2img(element);
6232       int graphic_2 = el2panelimg(element);
6233
6234       if (game.belt_dir[i] == MV_LEFT)
6235       {
6236         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6237         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6238       }
6239       else
6240       {
6241         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6242         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6243       }
6244     }
6245   }
6246
6247   SCAN_PLAYFIELD(x, y)
6248   {
6249     int element = Tile[x][y];
6250
6251     for (i = 0; i < NUM_BELTS; i++)
6252     {
6253       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6254       {
6255         int e_belt_nr = getBeltNrFromBeltElement(element);
6256         int belt_nr = i;
6257
6258         if (e_belt_nr == belt_nr)
6259         {
6260           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6261
6262           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6263         }
6264       }
6265     }
6266   }
6267 }
6268
6269 static void ToggleBeltSwitch(int x, int y)
6270 {
6271   static int belt_base_element[4] =
6272   {
6273     EL_CONVEYOR_BELT_1_LEFT,
6274     EL_CONVEYOR_BELT_2_LEFT,
6275     EL_CONVEYOR_BELT_3_LEFT,
6276     EL_CONVEYOR_BELT_4_LEFT
6277   };
6278   static int belt_base_active_element[4] =
6279   {
6280     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6281     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6282     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6283     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6284   };
6285   static int belt_base_switch_element[4] =
6286   {
6287     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6288     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6289     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6290     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6291   };
6292   static int belt_move_dir[4] =
6293   {
6294     MV_LEFT,
6295     MV_NONE,
6296     MV_RIGHT,
6297     MV_NONE,
6298   };
6299
6300   int element = Tile[x][y];
6301   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6302   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6303   int belt_dir = belt_move_dir[belt_dir_nr];
6304   int xx, yy, i;
6305
6306   if (!IS_BELT_SWITCH(element))
6307     return;
6308
6309   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6310   game.belt_dir[belt_nr] = belt_dir;
6311
6312   if (belt_dir_nr == 3)
6313     belt_dir_nr = 1;
6314
6315   // set frame order for belt animation graphic according to belt direction
6316   for (i = 0; i < NUM_BELT_PARTS; i++)
6317   {
6318     int element = belt_base_active_element[belt_nr] + i;
6319     int graphic_1 = el2img(element);
6320     int graphic_2 = el2panelimg(element);
6321
6322     if (belt_dir == MV_LEFT)
6323     {
6324       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6325       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6326     }
6327     else
6328     {
6329       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6330       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6331     }
6332   }
6333
6334   SCAN_PLAYFIELD(xx, yy)
6335   {
6336     int element = Tile[xx][yy];
6337
6338     if (IS_BELT_SWITCH(element))
6339     {
6340       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6341
6342       if (e_belt_nr == belt_nr)
6343       {
6344         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6345         TEST_DrawLevelField(xx, yy);
6346       }
6347     }
6348     else if (IS_BELT(element) && belt_dir != MV_NONE)
6349     {
6350       int e_belt_nr = getBeltNrFromBeltElement(element);
6351
6352       if (e_belt_nr == belt_nr)
6353       {
6354         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6355
6356         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6357         TEST_DrawLevelField(xx, yy);
6358       }
6359     }
6360     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6361     {
6362       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6363
6364       if (e_belt_nr == belt_nr)
6365       {
6366         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6367
6368         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6369         TEST_DrawLevelField(xx, yy);
6370       }
6371     }
6372   }
6373 }
6374
6375 static void ToggleSwitchgateSwitch(int x, int y)
6376 {
6377   int xx, yy;
6378
6379   game.switchgate_pos = !game.switchgate_pos;
6380
6381   SCAN_PLAYFIELD(xx, yy)
6382   {
6383     int element = Tile[xx][yy];
6384
6385     if (element == EL_SWITCHGATE_SWITCH_UP)
6386     {
6387       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6388       TEST_DrawLevelField(xx, yy);
6389     }
6390     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6391     {
6392       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6393       TEST_DrawLevelField(xx, yy);
6394     }
6395     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6396     {
6397       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6398       TEST_DrawLevelField(xx, yy);
6399     }
6400     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6401     {
6402       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6403       TEST_DrawLevelField(xx, yy);
6404     }
6405     else if (element == EL_SWITCHGATE_OPEN ||
6406              element == EL_SWITCHGATE_OPENING)
6407     {
6408       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6409
6410       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6411     }
6412     else if (element == EL_SWITCHGATE_CLOSED ||
6413              element == EL_SWITCHGATE_CLOSING)
6414     {
6415       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6416
6417       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6418     }
6419   }
6420 }
6421
6422 static int getInvisibleActiveFromInvisibleElement(int element)
6423 {
6424   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6425           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6426           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6427           element);
6428 }
6429
6430 static int getInvisibleFromInvisibleActiveElement(int element)
6431 {
6432   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6433           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6434           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6435           element);
6436 }
6437
6438 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6439 {
6440   int x, y;
6441
6442   SCAN_PLAYFIELD(x, y)
6443   {
6444     int element = Tile[x][y];
6445
6446     if (element == EL_LIGHT_SWITCH &&
6447         game.light_time_left > 0)
6448     {
6449       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6450       TEST_DrawLevelField(x, y);
6451     }
6452     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6453              game.light_time_left == 0)
6454     {
6455       Tile[x][y] = EL_LIGHT_SWITCH;
6456       TEST_DrawLevelField(x, y);
6457     }
6458     else if (element == EL_EMC_DRIPPER &&
6459              game.light_time_left > 0)
6460     {
6461       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6462       TEST_DrawLevelField(x, y);
6463     }
6464     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6465              game.light_time_left == 0)
6466     {
6467       Tile[x][y] = EL_EMC_DRIPPER;
6468       TEST_DrawLevelField(x, y);
6469     }
6470     else if (element == EL_INVISIBLE_STEELWALL ||
6471              element == EL_INVISIBLE_WALL ||
6472              element == EL_INVISIBLE_SAND)
6473     {
6474       if (game.light_time_left > 0)
6475         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6476
6477       TEST_DrawLevelField(x, y);
6478
6479       // uncrumble neighbour fields, if needed
6480       if (element == EL_INVISIBLE_SAND)
6481         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6482     }
6483     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6484              element == EL_INVISIBLE_WALL_ACTIVE ||
6485              element == EL_INVISIBLE_SAND_ACTIVE)
6486     {
6487       if (game.light_time_left == 0)
6488         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6489
6490       TEST_DrawLevelField(x, y);
6491
6492       // re-crumble neighbour fields, if needed
6493       if (element == EL_INVISIBLE_SAND)
6494         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6495     }
6496   }
6497 }
6498
6499 static void RedrawAllInvisibleElementsForLenses(void)
6500 {
6501   int x, y;
6502
6503   SCAN_PLAYFIELD(x, y)
6504   {
6505     int element = Tile[x][y];
6506
6507     if (element == EL_EMC_DRIPPER &&
6508         game.lenses_time_left > 0)
6509     {
6510       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6511       TEST_DrawLevelField(x, y);
6512     }
6513     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6514              game.lenses_time_left == 0)
6515     {
6516       Tile[x][y] = EL_EMC_DRIPPER;
6517       TEST_DrawLevelField(x, y);
6518     }
6519     else if (element == EL_INVISIBLE_STEELWALL ||
6520              element == EL_INVISIBLE_WALL ||
6521              element == EL_INVISIBLE_SAND)
6522     {
6523       if (game.lenses_time_left > 0)
6524         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6525
6526       TEST_DrawLevelField(x, y);
6527
6528       // uncrumble neighbour fields, if needed
6529       if (element == EL_INVISIBLE_SAND)
6530         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6531     }
6532     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6533              element == EL_INVISIBLE_WALL_ACTIVE ||
6534              element == EL_INVISIBLE_SAND_ACTIVE)
6535     {
6536       if (game.lenses_time_left == 0)
6537         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6538
6539       TEST_DrawLevelField(x, y);
6540
6541       // re-crumble neighbour fields, if needed
6542       if (element == EL_INVISIBLE_SAND)
6543         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6544     }
6545   }
6546 }
6547
6548 static void RedrawAllInvisibleElementsForMagnifier(void)
6549 {
6550   int x, y;
6551
6552   SCAN_PLAYFIELD(x, y)
6553   {
6554     int element = Tile[x][y];
6555
6556     if (element == EL_EMC_FAKE_GRASS &&
6557         game.magnify_time_left > 0)
6558     {
6559       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6560       TEST_DrawLevelField(x, y);
6561     }
6562     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6563              game.magnify_time_left == 0)
6564     {
6565       Tile[x][y] = EL_EMC_FAKE_GRASS;
6566       TEST_DrawLevelField(x, y);
6567     }
6568     else if (IS_GATE_GRAY(element) &&
6569              game.magnify_time_left > 0)
6570     {
6571       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6572                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6573                     IS_EM_GATE_GRAY(element) ?
6574                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6575                     IS_EMC_GATE_GRAY(element) ?
6576                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6577                     IS_DC_GATE_GRAY(element) ?
6578                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6579                     element);
6580       TEST_DrawLevelField(x, y);
6581     }
6582     else if (IS_GATE_GRAY_ACTIVE(element) &&
6583              game.magnify_time_left == 0)
6584     {
6585       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6586                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6587                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6588                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6589                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6590                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6591                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6592                     EL_DC_GATE_WHITE_GRAY :
6593                     element);
6594       TEST_DrawLevelField(x, y);
6595     }
6596   }
6597 }
6598
6599 static void ToggleLightSwitch(int x, int y)
6600 {
6601   int element = Tile[x][y];
6602
6603   game.light_time_left =
6604     (element == EL_LIGHT_SWITCH ?
6605      level.time_light * FRAMES_PER_SECOND : 0);
6606
6607   RedrawAllLightSwitchesAndInvisibleElements();
6608 }
6609
6610 static void ActivateTimegateSwitch(int x, int y)
6611 {
6612   int xx, yy;
6613
6614   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6615
6616   SCAN_PLAYFIELD(xx, yy)
6617   {
6618     int element = Tile[xx][yy];
6619
6620     if (element == EL_TIMEGATE_CLOSED ||
6621         element == EL_TIMEGATE_CLOSING)
6622     {
6623       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6624       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6625     }
6626
6627     /*
6628     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6629     {
6630       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6631       TEST_DrawLevelField(xx, yy);
6632     }
6633     */
6634
6635   }
6636
6637   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6638                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6639 }
6640
6641 static void Impact(int x, int y)
6642 {
6643   boolean last_line = (y == lev_fieldy - 1);
6644   boolean object_hit = FALSE;
6645   boolean impact = (last_line || object_hit);
6646   int element = Tile[x][y];
6647   int smashed = EL_STEELWALL;
6648
6649   if (!last_line)       // check if element below was hit
6650   {
6651     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6652       return;
6653
6654     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6655                                          MovDir[x][y + 1] != MV_DOWN ||
6656                                          MovPos[x][y + 1] <= TILEY / 2));
6657
6658     // do not smash moving elements that left the smashed field in time
6659     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6660         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6661       object_hit = FALSE;
6662
6663 #if USE_QUICKSAND_IMPACT_BUGFIX
6664     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6665     {
6666       RemoveMovingField(x, y + 1);
6667       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6668       Tile[x][y + 2] = EL_ROCK;
6669       TEST_DrawLevelField(x, y + 2);
6670
6671       object_hit = TRUE;
6672     }
6673
6674     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6675     {
6676       RemoveMovingField(x, y + 1);
6677       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6678       Tile[x][y + 2] = EL_ROCK;
6679       TEST_DrawLevelField(x, y + 2);
6680
6681       object_hit = TRUE;
6682     }
6683 #endif
6684
6685     if (object_hit)
6686       smashed = MovingOrBlocked2Element(x, y + 1);
6687
6688     impact = (last_line || object_hit);
6689   }
6690
6691   if (!last_line && smashed == EL_ACID) // element falls into acid
6692   {
6693     SplashAcid(x, y + 1);
6694     return;
6695   }
6696
6697   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6698   // only reset graphic animation if graphic really changes after impact
6699   if (impact &&
6700       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6701   {
6702     ResetGfxAnimation(x, y);
6703     TEST_DrawLevelField(x, y);
6704   }
6705
6706   if (impact && CAN_EXPLODE_IMPACT(element))
6707   {
6708     Bang(x, y);
6709     return;
6710   }
6711   else if (impact && element == EL_PEARL &&
6712            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6713   {
6714     ResetGfxAnimation(x, y);
6715
6716     Tile[x][y] = EL_PEARL_BREAKING;
6717     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6718     return;
6719   }
6720   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6721   {
6722     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6723
6724     return;
6725   }
6726
6727   if (impact && element == EL_AMOEBA_DROP)
6728   {
6729     if (object_hit && IS_PLAYER(x, y + 1))
6730       KillPlayerUnlessEnemyProtected(x, y + 1);
6731     else if (object_hit && smashed == EL_PENGUIN)
6732       Bang(x, y + 1);
6733     else
6734     {
6735       Tile[x][y] = EL_AMOEBA_GROWING;
6736       Store[x][y] = EL_AMOEBA_WET;
6737
6738       ResetRandomAnimationValue(x, y);
6739     }
6740     return;
6741   }
6742
6743   if (object_hit)               // check which object was hit
6744   {
6745     if ((CAN_PASS_MAGIC_WALL(element) && 
6746          (smashed == EL_MAGIC_WALL ||
6747           smashed == EL_BD_MAGIC_WALL)) ||
6748         (CAN_PASS_DC_MAGIC_WALL(element) &&
6749          smashed == EL_DC_MAGIC_WALL))
6750     {
6751       int xx, yy;
6752       int activated_magic_wall =
6753         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6754          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6755          EL_DC_MAGIC_WALL_ACTIVE);
6756
6757       // activate magic wall / mill
6758       SCAN_PLAYFIELD(xx, yy)
6759       {
6760         if (Tile[xx][yy] == smashed)
6761           Tile[xx][yy] = activated_magic_wall;
6762       }
6763
6764       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6765       game.magic_wall_active = TRUE;
6766
6767       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6768                             SND_MAGIC_WALL_ACTIVATING :
6769                             smashed == EL_BD_MAGIC_WALL ?
6770                             SND_BD_MAGIC_WALL_ACTIVATING :
6771                             SND_DC_MAGIC_WALL_ACTIVATING));
6772     }
6773
6774     if (IS_PLAYER(x, y + 1))
6775     {
6776       if (CAN_SMASH_PLAYER(element))
6777       {
6778         KillPlayerUnlessEnemyProtected(x, y + 1);
6779         return;
6780       }
6781     }
6782     else if (smashed == EL_PENGUIN)
6783     {
6784       if (CAN_SMASH_PLAYER(element))
6785       {
6786         Bang(x, y + 1);
6787         return;
6788       }
6789     }
6790     else if (element == EL_BD_DIAMOND)
6791     {
6792       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6793       {
6794         Bang(x, y + 1);
6795         return;
6796       }
6797     }
6798     else if (((element == EL_SP_INFOTRON ||
6799                element == EL_SP_ZONK) &&
6800               (smashed == EL_SP_SNIKSNAK ||
6801                smashed == EL_SP_ELECTRON ||
6802                smashed == EL_SP_DISK_ORANGE)) ||
6803              (element == EL_SP_INFOTRON &&
6804               smashed == EL_SP_DISK_YELLOW))
6805     {
6806       Bang(x, y + 1);
6807       return;
6808     }
6809     else if (CAN_SMASH_EVERYTHING(element))
6810     {
6811       if (IS_CLASSIC_ENEMY(smashed) ||
6812           CAN_EXPLODE_SMASHED(smashed))
6813       {
6814         Bang(x, y + 1);
6815         return;
6816       }
6817       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6818       {
6819         if (smashed == EL_LAMP ||
6820             smashed == EL_LAMP_ACTIVE)
6821         {
6822           Bang(x, y + 1);
6823           return;
6824         }
6825         else if (smashed == EL_NUT)
6826         {
6827           Tile[x][y + 1] = EL_NUT_BREAKING;
6828           PlayLevelSound(x, y, SND_NUT_BREAKING);
6829           RaiseScoreElement(EL_NUT);
6830           return;
6831         }
6832         else if (smashed == EL_PEARL)
6833         {
6834           ResetGfxAnimation(x, y);
6835
6836           Tile[x][y + 1] = EL_PEARL_BREAKING;
6837           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6838           return;
6839         }
6840         else if (smashed == EL_DIAMOND)
6841         {
6842           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6843           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6844           return;
6845         }
6846         else if (IS_BELT_SWITCH(smashed))
6847         {
6848           ToggleBeltSwitch(x, y + 1);
6849         }
6850         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6851                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6852                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6853                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6854         {
6855           ToggleSwitchgateSwitch(x, y + 1);
6856         }
6857         else if (smashed == EL_LIGHT_SWITCH ||
6858                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6859         {
6860           ToggleLightSwitch(x, y + 1);
6861         }
6862         else
6863         {
6864           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6865
6866           CheckElementChangeBySide(x, y + 1, smashed, element,
6867                                    CE_SWITCHED, CH_SIDE_TOP);
6868           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6869                                             CH_SIDE_TOP);
6870         }
6871       }
6872       else
6873       {
6874         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6875       }
6876     }
6877   }
6878
6879   // play sound of magic wall / mill
6880   if (!last_line &&
6881       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6882        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6883        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6884   {
6885     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6886       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6887     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6888       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6889     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6890       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6891
6892     return;
6893   }
6894
6895   // play sound of object that hits the ground
6896   if (last_line || object_hit)
6897     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6898 }
6899
6900 static void TurnRoundExt(int x, int y)
6901 {
6902   static struct
6903   {
6904     int dx, dy;
6905   } move_xy[] =
6906   {
6907     {  0,  0 },
6908     { -1,  0 },
6909     { +1,  0 },
6910     {  0,  0 },
6911     {  0, -1 },
6912     {  0,  0 }, { 0, 0 }, { 0, 0 },
6913     {  0, +1 }
6914   };
6915   static struct
6916   {
6917     int left, right, back;
6918   } turn[] =
6919   {
6920     { 0,        0,              0        },
6921     { MV_DOWN,  MV_UP,          MV_RIGHT },
6922     { MV_UP,    MV_DOWN,        MV_LEFT  },
6923     { 0,        0,              0        },
6924     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6925     { 0,        0,              0        },
6926     { 0,        0,              0        },
6927     { 0,        0,              0        },
6928     { MV_RIGHT, MV_LEFT,        MV_UP    }
6929   };
6930
6931   int element = Tile[x][y];
6932   int move_pattern = element_info[element].move_pattern;
6933
6934   int old_move_dir = MovDir[x][y];
6935   int left_dir  = turn[old_move_dir].left;
6936   int right_dir = turn[old_move_dir].right;
6937   int back_dir  = turn[old_move_dir].back;
6938
6939   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6940   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6941   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6942   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6943
6944   int left_x  = x + left_dx,  left_y  = y + left_dy;
6945   int right_x = x + right_dx, right_y = y + right_dy;
6946   int move_x  = x + move_dx,  move_y  = y + move_dy;
6947
6948   int xx, yy;
6949
6950   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6951   {
6952     TestIfBadThingTouchesOtherBadThing(x, y);
6953
6954     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6955       MovDir[x][y] = right_dir;
6956     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6957       MovDir[x][y] = left_dir;
6958
6959     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6960       MovDelay[x][y] = 9;
6961     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6962       MovDelay[x][y] = 1;
6963   }
6964   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6965   {
6966     TestIfBadThingTouchesOtherBadThing(x, y);
6967
6968     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6969       MovDir[x][y] = left_dir;
6970     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6971       MovDir[x][y] = right_dir;
6972
6973     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6974       MovDelay[x][y] = 9;
6975     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6976       MovDelay[x][y] = 1;
6977   }
6978   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6979   {
6980     TestIfBadThingTouchesOtherBadThing(x, y);
6981
6982     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6983       MovDir[x][y] = left_dir;
6984     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6985       MovDir[x][y] = right_dir;
6986
6987     if (MovDir[x][y] != old_move_dir)
6988       MovDelay[x][y] = 9;
6989   }
6990   else if (element == EL_YAMYAM)
6991   {
6992     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6993     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6994
6995     if (can_turn_left && can_turn_right)
6996       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6997     else if (can_turn_left)
6998       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6999     else if (can_turn_right)
7000       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7001     else
7002       MovDir[x][y] = back_dir;
7003
7004     MovDelay[x][y] = 16 + 16 * RND(3);
7005   }
7006   else if (element == EL_DARK_YAMYAM)
7007   {
7008     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7009                                                          left_x, left_y);
7010     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7011                                                          right_x, right_y);
7012
7013     if (can_turn_left && can_turn_right)
7014       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7015     else if (can_turn_left)
7016       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7017     else if (can_turn_right)
7018       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7019     else
7020       MovDir[x][y] = back_dir;
7021
7022     MovDelay[x][y] = 16 + 16 * RND(3);
7023   }
7024   else if (element == EL_PACMAN)
7025   {
7026     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7027     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7028
7029     if (can_turn_left && can_turn_right)
7030       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7031     else if (can_turn_left)
7032       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7033     else if (can_turn_right)
7034       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7035     else
7036       MovDir[x][y] = back_dir;
7037
7038     MovDelay[x][y] = 6 + RND(40);
7039   }
7040   else if (element == EL_PIG)
7041   {
7042     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7043     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7044     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7045     boolean should_turn_left, should_turn_right, should_move_on;
7046     int rnd_value = 24;
7047     int rnd = RND(rnd_value);
7048
7049     should_turn_left = (can_turn_left &&
7050                         (!can_move_on ||
7051                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7052                                                    y + back_dy + left_dy)));
7053     should_turn_right = (can_turn_right &&
7054                          (!can_move_on ||
7055                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7056                                                     y + back_dy + right_dy)));
7057     should_move_on = (can_move_on &&
7058                       (!can_turn_left ||
7059                        !can_turn_right ||
7060                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7061                                                  y + move_dy + left_dy) ||
7062                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7063                                                  y + move_dy + right_dy)));
7064
7065     if (should_turn_left || should_turn_right || should_move_on)
7066     {
7067       if (should_turn_left && should_turn_right && should_move_on)
7068         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7069                         rnd < 2 * rnd_value / 3 ? right_dir :
7070                         old_move_dir);
7071       else if (should_turn_left && should_turn_right)
7072         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7073       else if (should_turn_left && should_move_on)
7074         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7075       else if (should_turn_right && should_move_on)
7076         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7077       else if (should_turn_left)
7078         MovDir[x][y] = left_dir;
7079       else if (should_turn_right)
7080         MovDir[x][y] = right_dir;
7081       else if (should_move_on)
7082         MovDir[x][y] = old_move_dir;
7083     }
7084     else if (can_move_on && rnd > rnd_value / 8)
7085       MovDir[x][y] = old_move_dir;
7086     else if (can_turn_left && can_turn_right)
7087       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7088     else if (can_turn_left && rnd > rnd_value / 8)
7089       MovDir[x][y] = left_dir;
7090     else if (can_turn_right && rnd > rnd_value/8)
7091       MovDir[x][y] = right_dir;
7092     else
7093       MovDir[x][y] = back_dir;
7094
7095     xx = x + move_xy[MovDir[x][y]].dx;
7096     yy = y + move_xy[MovDir[x][y]].dy;
7097
7098     if (!IN_LEV_FIELD(xx, yy) ||
7099         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7100       MovDir[x][y] = old_move_dir;
7101
7102     MovDelay[x][y] = 0;
7103   }
7104   else if (element == EL_DRAGON)
7105   {
7106     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7107     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7108     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7109     int rnd_value = 24;
7110     int rnd = RND(rnd_value);
7111
7112     if (can_move_on && rnd > rnd_value / 8)
7113       MovDir[x][y] = old_move_dir;
7114     else if (can_turn_left && can_turn_right)
7115       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7116     else if (can_turn_left && rnd > rnd_value / 8)
7117       MovDir[x][y] = left_dir;
7118     else if (can_turn_right && rnd > rnd_value / 8)
7119       MovDir[x][y] = right_dir;
7120     else
7121       MovDir[x][y] = back_dir;
7122
7123     xx = x + move_xy[MovDir[x][y]].dx;
7124     yy = y + move_xy[MovDir[x][y]].dy;
7125
7126     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7127       MovDir[x][y] = old_move_dir;
7128
7129     MovDelay[x][y] = 0;
7130   }
7131   else if (element == EL_MOLE)
7132   {
7133     boolean can_move_on =
7134       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7135                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7136                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7137     if (!can_move_on)
7138     {
7139       boolean can_turn_left =
7140         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7141                               IS_AMOEBOID(Tile[left_x][left_y])));
7142
7143       boolean can_turn_right =
7144         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7145                               IS_AMOEBOID(Tile[right_x][right_y])));
7146
7147       if (can_turn_left && can_turn_right)
7148         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7149       else if (can_turn_left)
7150         MovDir[x][y] = left_dir;
7151       else
7152         MovDir[x][y] = right_dir;
7153     }
7154
7155     if (MovDir[x][y] != old_move_dir)
7156       MovDelay[x][y] = 9;
7157   }
7158   else if (element == EL_BALLOON)
7159   {
7160     MovDir[x][y] = game.wind_direction;
7161     MovDelay[x][y] = 0;
7162   }
7163   else if (element == EL_SPRING)
7164   {
7165     if (MovDir[x][y] & MV_HORIZONTAL)
7166     {
7167       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7168           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7169       {
7170         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7171         ResetGfxAnimation(move_x, move_y);
7172         TEST_DrawLevelField(move_x, move_y);
7173
7174         MovDir[x][y] = back_dir;
7175       }
7176       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7177                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7178         MovDir[x][y] = MV_NONE;
7179     }
7180
7181     MovDelay[x][y] = 0;
7182   }
7183   else if (element == EL_ROBOT ||
7184            element == EL_SATELLITE ||
7185            element == EL_PENGUIN ||
7186            element == EL_EMC_ANDROID)
7187   {
7188     int attr_x = -1, attr_y = -1;
7189
7190     if (game.all_players_gone)
7191     {
7192       attr_x = game.exit_x;
7193       attr_y = game.exit_y;
7194     }
7195     else
7196     {
7197       int i;
7198
7199       for (i = 0; i < MAX_PLAYERS; i++)
7200       {
7201         struct PlayerInfo *player = &stored_player[i];
7202         int jx = player->jx, jy = player->jy;
7203
7204         if (!player->active)
7205           continue;
7206
7207         if (attr_x == -1 ||
7208             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7209         {
7210           attr_x = jx;
7211           attr_y = jy;
7212         }
7213       }
7214     }
7215
7216     if (element == EL_ROBOT &&
7217         game.robot_wheel_x >= 0 &&
7218         game.robot_wheel_y >= 0 &&
7219         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7220          game.engine_version < VERSION_IDENT(3,1,0,0)))
7221     {
7222       attr_x = game.robot_wheel_x;
7223       attr_y = game.robot_wheel_y;
7224     }
7225
7226     if (element == EL_PENGUIN)
7227     {
7228       int i;
7229       static int xy[4][2] =
7230       {
7231         { 0, -1 },
7232         { -1, 0 },
7233         { +1, 0 },
7234         { 0, +1 }
7235       };
7236
7237       for (i = 0; i < NUM_DIRECTIONS; i++)
7238       {
7239         int ex = x + xy[i][0];
7240         int ey = y + xy[i][1];
7241
7242         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7243                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7244                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7245                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7246         {
7247           attr_x = ex;
7248           attr_y = ey;
7249           break;
7250         }
7251       }
7252     }
7253
7254     MovDir[x][y] = MV_NONE;
7255     if (attr_x < x)
7256       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7257     else if (attr_x > x)
7258       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7259     if (attr_y < y)
7260       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7261     else if (attr_y > y)
7262       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7263
7264     if (element == EL_ROBOT)
7265     {
7266       int newx, newy;
7267
7268       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7269         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7270       Moving2Blocked(x, y, &newx, &newy);
7271
7272       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7273         MovDelay[x][y] = 8 + 8 * !RND(3);
7274       else
7275         MovDelay[x][y] = 16;
7276     }
7277     else if (element == EL_PENGUIN)
7278     {
7279       int newx, newy;
7280
7281       MovDelay[x][y] = 1;
7282
7283       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7284       {
7285         boolean first_horiz = RND(2);
7286         int new_move_dir = MovDir[x][y];
7287
7288         MovDir[x][y] =
7289           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7290         Moving2Blocked(x, y, &newx, &newy);
7291
7292         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7293           return;
7294
7295         MovDir[x][y] =
7296           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7297         Moving2Blocked(x, y, &newx, &newy);
7298
7299         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7300           return;
7301
7302         MovDir[x][y] = old_move_dir;
7303         return;
7304       }
7305     }
7306     else if (element == EL_SATELLITE)
7307     {
7308       int newx, newy;
7309
7310       MovDelay[x][y] = 1;
7311
7312       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7313       {
7314         boolean first_horiz = RND(2);
7315         int new_move_dir = MovDir[x][y];
7316
7317         MovDir[x][y] =
7318           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7319         Moving2Blocked(x, y, &newx, &newy);
7320
7321         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7322           return;
7323
7324         MovDir[x][y] =
7325           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7326         Moving2Blocked(x, y, &newx, &newy);
7327
7328         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7329           return;
7330
7331         MovDir[x][y] = old_move_dir;
7332         return;
7333       }
7334     }
7335     else if (element == EL_EMC_ANDROID)
7336     {
7337       static int check_pos[16] =
7338       {
7339         -1,             //  0 => (invalid)
7340         7,              //  1 => MV_LEFT
7341         3,              //  2 => MV_RIGHT
7342         -1,             //  3 => (invalid)
7343         1,              //  4 =>            MV_UP
7344         0,              //  5 => MV_LEFT  | MV_UP
7345         2,              //  6 => MV_RIGHT | MV_UP
7346         -1,             //  7 => (invalid)
7347         5,              //  8 =>            MV_DOWN
7348         6,              //  9 => MV_LEFT  | MV_DOWN
7349         4,              // 10 => MV_RIGHT | MV_DOWN
7350         -1,             // 11 => (invalid)
7351         -1,             // 12 => (invalid)
7352         -1,             // 13 => (invalid)
7353         -1,             // 14 => (invalid)
7354         -1,             // 15 => (invalid)
7355       };
7356       static struct
7357       {
7358         int dx, dy;
7359         int dir;
7360       } check_xy[8] =
7361       {
7362         { -1, -1,       MV_LEFT  | MV_UP   },
7363         {  0, -1,                  MV_UP   },
7364         { +1, -1,       MV_RIGHT | MV_UP   },
7365         { +1,  0,       MV_RIGHT           },
7366         { +1, +1,       MV_RIGHT | MV_DOWN },
7367         {  0, +1,                  MV_DOWN },
7368         { -1, +1,       MV_LEFT  | MV_DOWN },
7369         { -1,  0,       MV_LEFT            },
7370       };
7371       int start_pos, check_order;
7372       boolean can_clone = FALSE;
7373       int i;
7374
7375       // check if there is any free field around current position
7376       for (i = 0; i < 8; i++)
7377       {
7378         int newx = x + check_xy[i].dx;
7379         int newy = y + check_xy[i].dy;
7380
7381         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7382         {
7383           can_clone = TRUE;
7384
7385           break;
7386         }
7387       }
7388
7389       if (can_clone)            // randomly find an element to clone
7390       {
7391         can_clone = FALSE;
7392
7393         start_pos = check_pos[RND(8)];
7394         check_order = (RND(2) ? -1 : +1);
7395
7396         for (i = 0; i < 8; i++)
7397         {
7398           int pos_raw = start_pos + i * check_order;
7399           int pos = (pos_raw + 8) % 8;
7400           int newx = x + check_xy[pos].dx;
7401           int newy = y + check_xy[pos].dy;
7402
7403           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7404           {
7405             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7406             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7407
7408             Store[x][y] = Tile[newx][newy];
7409
7410             can_clone = TRUE;
7411
7412             break;
7413           }
7414         }
7415       }
7416
7417       if (can_clone)            // randomly find a direction to move
7418       {
7419         can_clone = FALSE;
7420
7421         start_pos = check_pos[RND(8)];
7422         check_order = (RND(2) ? -1 : +1);
7423
7424         for (i = 0; i < 8; i++)
7425         {
7426           int pos_raw = start_pos + i * check_order;
7427           int pos = (pos_raw + 8) % 8;
7428           int newx = x + check_xy[pos].dx;
7429           int newy = y + check_xy[pos].dy;
7430           int new_move_dir = check_xy[pos].dir;
7431
7432           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7433           {
7434             MovDir[x][y] = new_move_dir;
7435             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7436
7437             can_clone = TRUE;
7438
7439             break;
7440           }
7441         }
7442       }
7443
7444       if (can_clone)            // cloning and moving successful
7445         return;
7446
7447       // cannot clone -- try to move towards player
7448
7449       start_pos = check_pos[MovDir[x][y] & 0x0f];
7450       check_order = (RND(2) ? -1 : +1);
7451
7452       for (i = 0; i < 3; i++)
7453       {
7454         // first check start_pos, then previous/next or (next/previous) pos
7455         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7456         int pos = (pos_raw + 8) % 8;
7457         int newx = x + check_xy[pos].dx;
7458         int newy = y + check_xy[pos].dy;
7459         int new_move_dir = check_xy[pos].dir;
7460
7461         if (IS_PLAYER(newx, newy))
7462           break;
7463
7464         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7465         {
7466           MovDir[x][y] = new_move_dir;
7467           MovDelay[x][y] = level.android_move_time * 8 + 1;
7468
7469           break;
7470         }
7471       }
7472     }
7473   }
7474   else if (move_pattern == MV_TURNING_LEFT ||
7475            move_pattern == MV_TURNING_RIGHT ||
7476            move_pattern == MV_TURNING_LEFT_RIGHT ||
7477            move_pattern == MV_TURNING_RIGHT_LEFT ||
7478            move_pattern == MV_TURNING_RANDOM ||
7479            move_pattern == MV_ALL_DIRECTIONS)
7480   {
7481     boolean can_turn_left =
7482       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7483     boolean can_turn_right =
7484       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7485
7486     if (element_info[element].move_stepsize == 0)       // "not moving"
7487       return;
7488
7489     if (move_pattern == MV_TURNING_LEFT)
7490       MovDir[x][y] = left_dir;
7491     else if (move_pattern == MV_TURNING_RIGHT)
7492       MovDir[x][y] = right_dir;
7493     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7494       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7495     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7496       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7497     else if (move_pattern == MV_TURNING_RANDOM)
7498       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7499                       can_turn_right && !can_turn_left ? right_dir :
7500                       RND(2) ? left_dir : right_dir);
7501     else if (can_turn_left && can_turn_right)
7502       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7503     else if (can_turn_left)
7504       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7505     else if (can_turn_right)
7506       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7507     else
7508       MovDir[x][y] = back_dir;
7509
7510     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7511   }
7512   else if (move_pattern == MV_HORIZONTAL ||
7513            move_pattern == MV_VERTICAL)
7514   {
7515     if (move_pattern & old_move_dir)
7516       MovDir[x][y] = back_dir;
7517     else if (move_pattern == MV_HORIZONTAL)
7518       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7519     else if (move_pattern == MV_VERTICAL)
7520       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7521
7522     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7523   }
7524   else if (move_pattern & MV_ANY_DIRECTION)
7525   {
7526     MovDir[x][y] = move_pattern;
7527     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7528   }
7529   else if (move_pattern & MV_WIND_DIRECTION)
7530   {
7531     MovDir[x][y] = game.wind_direction;
7532     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7533   }
7534   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7535   {
7536     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7537       MovDir[x][y] = left_dir;
7538     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7539       MovDir[x][y] = right_dir;
7540
7541     if (MovDir[x][y] != old_move_dir)
7542       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7543   }
7544   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7545   {
7546     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7547       MovDir[x][y] = right_dir;
7548     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7549       MovDir[x][y] = left_dir;
7550
7551     if (MovDir[x][y] != old_move_dir)
7552       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7553   }
7554   else if (move_pattern == MV_TOWARDS_PLAYER ||
7555            move_pattern == MV_AWAY_FROM_PLAYER)
7556   {
7557     int attr_x = -1, attr_y = -1;
7558     int newx, newy;
7559     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7560
7561     if (game.all_players_gone)
7562     {
7563       attr_x = game.exit_x;
7564       attr_y = game.exit_y;
7565     }
7566     else
7567     {
7568       int i;
7569
7570       for (i = 0; i < MAX_PLAYERS; i++)
7571       {
7572         struct PlayerInfo *player = &stored_player[i];
7573         int jx = player->jx, jy = player->jy;
7574
7575         if (!player->active)
7576           continue;
7577
7578         if (attr_x == -1 ||
7579             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7580         {
7581           attr_x = jx;
7582           attr_y = jy;
7583         }
7584       }
7585     }
7586
7587     MovDir[x][y] = MV_NONE;
7588     if (attr_x < x)
7589       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7590     else if (attr_x > x)
7591       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7592     if (attr_y < y)
7593       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7594     else if (attr_y > y)
7595       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7596
7597     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7598
7599     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7600     {
7601       boolean first_horiz = RND(2);
7602       int new_move_dir = MovDir[x][y];
7603
7604       if (element_info[element].move_stepsize == 0)     // "not moving"
7605       {
7606         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7607         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7608
7609         return;
7610       }
7611
7612       MovDir[x][y] =
7613         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7614       Moving2Blocked(x, y, &newx, &newy);
7615
7616       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7617         return;
7618
7619       MovDir[x][y] =
7620         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7621       Moving2Blocked(x, y, &newx, &newy);
7622
7623       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7624         return;
7625
7626       MovDir[x][y] = old_move_dir;
7627     }
7628   }
7629   else if (move_pattern == MV_WHEN_PUSHED ||
7630            move_pattern == MV_WHEN_DROPPED)
7631   {
7632     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7633       MovDir[x][y] = MV_NONE;
7634
7635     MovDelay[x][y] = 0;
7636   }
7637   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7638   {
7639     static int test_xy[7][2] =
7640     {
7641       { 0, -1 },
7642       { -1, 0 },
7643       { +1, 0 },
7644       { 0, +1 },
7645       { 0, -1 },
7646       { -1, 0 },
7647       { +1, 0 },
7648     };
7649     static int test_dir[7] =
7650     {
7651       MV_UP,
7652       MV_LEFT,
7653       MV_RIGHT,
7654       MV_DOWN,
7655       MV_UP,
7656       MV_LEFT,
7657       MV_RIGHT,
7658     };
7659     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7660     int move_preference = -1000000;     // start with very low preference
7661     int new_move_dir = MV_NONE;
7662     int start_test = RND(4);
7663     int i;
7664
7665     for (i = 0; i < NUM_DIRECTIONS; i++)
7666     {
7667       int move_dir = test_dir[start_test + i];
7668       int move_dir_preference;
7669
7670       xx = x + test_xy[start_test + i][0];
7671       yy = y + test_xy[start_test + i][1];
7672
7673       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7674           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7675       {
7676         new_move_dir = move_dir;
7677
7678         break;
7679       }
7680
7681       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7682         continue;
7683
7684       move_dir_preference = -1 * RunnerVisit[xx][yy];
7685       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7686         move_dir_preference = PlayerVisit[xx][yy];
7687
7688       if (move_dir_preference > move_preference)
7689       {
7690         // prefer field that has not been visited for the longest time
7691         move_preference = move_dir_preference;
7692         new_move_dir = move_dir;
7693       }
7694       else if (move_dir_preference == move_preference &&
7695                move_dir == old_move_dir)
7696       {
7697         // prefer last direction when all directions are preferred equally
7698         move_preference = move_dir_preference;
7699         new_move_dir = move_dir;
7700       }
7701     }
7702
7703     MovDir[x][y] = new_move_dir;
7704     if (old_move_dir != new_move_dir)
7705       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7706   }
7707 }
7708
7709 static void TurnRound(int x, int y)
7710 {
7711   int direction = MovDir[x][y];
7712
7713   TurnRoundExt(x, y);
7714
7715   GfxDir[x][y] = MovDir[x][y];
7716
7717   if (direction != MovDir[x][y])
7718     GfxFrame[x][y] = 0;
7719
7720   if (MovDelay[x][y])
7721     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7722
7723   ResetGfxFrame(x, y);
7724 }
7725
7726 static boolean JustBeingPushed(int x, int y)
7727 {
7728   int i;
7729
7730   for (i = 0; i < MAX_PLAYERS; i++)
7731   {
7732     struct PlayerInfo *player = &stored_player[i];
7733
7734     if (player->active && player->is_pushing && player->MovPos)
7735     {
7736       int next_jx = player->jx + (player->jx - player->last_jx);
7737       int next_jy = player->jy + (player->jy - player->last_jy);
7738
7739       if (x == next_jx && y == next_jy)
7740         return TRUE;
7741     }
7742   }
7743
7744   return FALSE;
7745 }
7746
7747 static void StartMoving(int x, int y)
7748 {
7749   boolean started_moving = FALSE;       // some elements can fall _and_ move
7750   int element = Tile[x][y];
7751
7752   if (Stop[x][y])
7753     return;
7754
7755   if (MovDelay[x][y] == 0)
7756     GfxAction[x][y] = ACTION_DEFAULT;
7757
7758   if (CAN_FALL(element) && y < lev_fieldy - 1)
7759   {
7760     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7761         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7762       if (JustBeingPushed(x, y))
7763         return;
7764
7765     if (element == EL_QUICKSAND_FULL)
7766     {
7767       if (IS_FREE(x, y + 1))
7768       {
7769         InitMovingField(x, y, MV_DOWN);
7770         started_moving = TRUE;
7771
7772         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7773 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7774         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7775           Store[x][y] = EL_ROCK;
7776 #else
7777         Store[x][y] = EL_ROCK;
7778 #endif
7779
7780         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7781       }
7782       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7783       {
7784         if (!MovDelay[x][y])
7785         {
7786           MovDelay[x][y] = TILEY + 1;
7787
7788           ResetGfxAnimation(x, y);
7789           ResetGfxAnimation(x, y + 1);
7790         }
7791
7792         if (MovDelay[x][y])
7793         {
7794           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7795           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7796
7797           MovDelay[x][y]--;
7798           if (MovDelay[x][y])
7799             return;
7800         }
7801
7802         Tile[x][y] = EL_QUICKSAND_EMPTY;
7803         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7804         Store[x][y + 1] = Store[x][y];
7805         Store[x][y] = 0;
7806
7807         PlayLevelSoundAction(x, y, ACTION_FILLING);
7808       }
7809       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7810       {
7811         if (!MovDelay[x][y])
7812         {
7813           MovDelay[x][y] = TILEY + 1;
7814
7815           ResetGfxAnimation(x, y);
7816           ResetGfxAnimation(x, y + 1);
7817         }
7818
7819         if (MovDelay[x][y])
7820         {
7821           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7822           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7823
7824           MovDelay[x][y]--;
7825           if (MovDelay[x][y])
7826             return;
7827         }
7828
7829         Tile[x][y] = EL_QUICKSAND_EMPTY;
7830         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7831         Store[x][y + 1] = Store[x][y];
7832         Store[x][y] = 0;
7833
7834         PlayLevelSoundAction(x, y, ACTION_FILLING);
7835       }
7836     }
7837     else if (element == EL_QUICKSAND_FAST_FULL)
7838     {
7839       if (IS_FREE(x, y + 1))
7840       {
7841         InitMovingField(x, y, MV_DOWN);
7842         started_moving = TRUE;
7843
7844         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7845 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7846         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7847           Store[x][y] = EL_ROCK;
7848 #else
7849         Store[x][y] = EL_ROCK;
7850 #endif
7851
7852         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7853       }
7854       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7855       {
7856         if (!MovDelay[x][y])
7857         {
7858           MovDelay[x][y] = TILEY + 1;
7859
7860           ResetGfxAnimation(x, y);
7861           ResetGfxAnimation(x, y + 1);
7862         }
7863
7864         if (MovDelay[x][y])
7865         {
7866           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7867           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7868
7869           MovDelay[x][y]--;
7870           if (MovDelay[x][y])
7871             return;
7872         }
7873
7874         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7875         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7876         Store[x][y + 1] = Store[x][y];
7877         Store[x][y] = 0;
7878
7879         PlayLevelSoundAction(x, y, ACTION_FILLING);
7880       }
7881       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7882       {
7883         if (!MovDelay[x][y])
7884         {
7885           MovDelay[x][y] = TILEY + 1;
7886
7887           ResetGfxAnimation(x, y);
7888           ResetGfxAnimation(x, y + 1);
7889         }
7890
7891         if (MovDelay[x][y])
7892         {
7893           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7894           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7895
7896           MovDelay[x][y]--;
7897           if (MovDelay[x][y])
7898             return;
7899         }
7900
7901         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7902         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7903         Store[x][y + 1] = Store[x][y];
7904         Store[x][y] = 0;
7905
7906         PlayLevelSoundAction(x, y, ACTION_FILLING);
7907       }
7908     }
7909     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7910              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7911     {
7912       InitMovingField(x, y, MV_DOWN);
7913       started_moving = TRUE;
7914
7915       Tile[x][y] = EL_QUICKSAND_FILLING;
7916       Store[x][y] = element;
7917
7918       PlayLevelSoundAction(x, y, ACTION_FILLING);
7919     }
7920     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7921              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7922     {
7923       InitMovingField(x, y, MV_DOWN);
7924       started_moving = TRUE;
7925
7926       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7927       Store[x][y] = element;
7928
7929       PlayLevelSoundAction(x, y, ACTION_FILLING);
7930     }
7931     else if (element == EL_MAGIC_WALL_FULL)
7932     {
7933       if (IS_FREE(x, y + 1))
7934       {
7935         InitMovingField(x, y, MV_DOWN);
7936         started_moving = TRUE;
7937
7938         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7939         Store[x][y] = EL_CHANGED(Store[x][y]);
7940       }
7941       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7942       {
7943         if (!MovDelay[x][y])
7944           MovDelay[x][y] = TILEY / 4 + 1;
7945
7946         if (MovDelay[x][y])
7947         {
7948           MovDelay[x][y]--;
7949           if (MovDelay[x][y])
7950             return;
7951         }
7952
7953         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7954         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7955         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7956         Store[x][y] = 0;
7957       }
7958     }
7959     else if (element == EL_BD_MAGIC_WALL_FULL)
7960     {
7961       if (IS_FREE(x, y + 1))
7962       {
7963         InitMovingField(x, y, MV_DOWN);
7964         started_moving = TRUE;
7965
7966         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7967         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7968       }
7969       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7970       {
7971         if (!MovDelay[x][y])
7972           MovDelay[x][y] = TILEY / 4 + 1;
7973
7974         if (MovDelay[x][y])
7975         {
7976           MovDelay[x][y]--;
7977           if (MovDelay[x][y])
7978             return;
7979         }
7980
7981         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7982         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7983         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7984         Store[x][y] = 0;
7985       }
7986     }
7987     else if (element == EL_DC_MAGIC_WALL_FULL)
7988     {
7989       if (IS_FREE(x, y + 1))
7990       {
7991         InitMovingField(x, y, MV_DOWN);
7992         started_moving = TRUE;
7993
7994         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7995         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7996       }
7997       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7998       {
7999         if (!MovDelay[x][y])
8000           MovDelay[x][y] = TILEY / 4 + 1;
8001
8002         if (MovDelay[x][y])
8003         {
8004           MovDelay[x][y]--;
8005           if (MovDelay[x][y])
8006             return;
8007         }
8008
8009         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8010         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8011         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8012         Store[x][y] = 0;
8013       }
8014     }
8015     else if ((CAN_PASS_MAGIC_WALL(element) &&
8016               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8017                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8018              (CAN_PASS_DC_MAGIC_WALL(element) &&
8019               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8020
8021     {
8022       InitMovingField(x, y, MV_DOWN);
8023       started_moving = TRUE;
8024
8025       Tile[x][y] =
8026         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8027          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8028          EL_DC_MAGIC_WALL_FILLING);
8029       Store[x][y] = element;
8030     }
8031     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8032     {
8033       SplashAcid(x, y + 1);
8034
8035       InitMovingField(x, y, MV_DOWN);
8036       started_moving = TRUE;
8037
8038       Store[x][y] = EL_ACID;
8039     }
8040     else if (
8041              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8042               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8043              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8044               CAN_FALL(element) && WasJustFalling[x][y] &&
8045               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8046
8047              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8048               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8049               (Tile[x][y + 1] == EL_BLOCKED)))
8050     {
8051       /* this is needed for a special case not covered by calling "Impact()"
8052          from "ContinueMoving()": if an element moves to a tile directly below
8053          another element which was just falling on that tile (which was empty
8054          in the previous frame), the falling element above would just stop
8055          instead of smashing the element below (in previous version, the above
8056          element was just checked for "moving" instead of "falling", resulting
8057          in incorrect smashes caused by horizontal movement of the above
8058          element; also, the case of the player being the element to smash was
8059          simply not covered here... :-/ ) */
8060
8061       CheckCollision[x][y] = 0;
8062       CheckImpact[x][y] = 0;
8063
8064       Impact(x, y);
8065     }
8066     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8067     {
8068       if (MovDir[x][y] == MV_NONE)
8069       {
8070         InitMovingField(x, y, MV_DOWN);
8071         started_moving = TRUE;
8072       }
8073     }
8074     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8075     {
8076       if (WasJustFalling[x][y]) // prevent animation from being restarted
8077         MovDir[x][y] = MV_DOWN;
8078
8079       InitMovingField(x, y, MV_DOWN);
8080       started_moving = TRUE;
8081     }
8082     else if (element == EL_AMOEBA_DROP)
8083     {
8084       Tile[x][y] = EL_AMOEBA_GROWING;
8085       Store[x][y] = EL_AMOEBA_WET;
8086     }
8087     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8088               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8089              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8090              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8091     {
8092       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8093                                 (IS_FREE(x - 1, y + 1) ||
8094                                  Tile[x - 1][y + 1] == EL_ACID));
8095       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8096                                 (IS_FREE(x + 1, y + 1) ||
8097                                  Tile[x + 1][y + 1] == EL_ACID));
8098       boolean can_fall_any  = (can_fall_left || can_fall_right);
8099       boolean can_fall_both = (can_fall_left && can_fall_right);
8100       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8101
8102       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8103       {
8104         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8105           can_fall_right = FALSE;
8106         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8107           can_fall_left = FALSE;
8108         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8109           can_fall_right = FALSE;
8110         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8111           can_fall_left = FALSE;
8112
8113         can_fall_any  = (can_fall_left || can_fall_right);
8114         can_fall_both = FALSE;
8115       }
8116
8117       if (can_fall_both)
8118       {
8119         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8120           can_fall_right = FALSE;       // slip down on left side
8121         else
8122           can_fall_left = !(can_fall_right = RND(2));
8123
8124         can_fall_both = FALSE;
8125       }
8126
8127       if (can_fall_any)
8128       {
8129         // if not determined otherwise, prefer left side for slipping down
8130         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8131         started_moving = TRUE;
8132       }
8133     }
8134     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8135     {
8136       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8137       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8138       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8139       int belt_dir = game.belt_dir[belt_nr];
8140
8141       if ((belt_dir == MV_LEFT  && left_is_free) ||
8142           (belt_dir == MV_RIGHT && right_is_free))
8143       {
8144         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8145
8146         InitMovingField(x, y, belt_dir);
8147         started_moving = TRUE;
8148
8149         Pushed[x][y] = TRUE;
8150         Pushed[nextx][y] = TRUE;
8151
8152         GfxAction[x][y] = ACTION_DEFAULT;
8153       }
8154       else
8155       {
8156         MovDir[x][y] = 0;       // if element was moving, stop it
8157       }
8158     }
8159   }
8160
8161   // not "else if" because of elements that can fall and move (EL_SPRING)
8162   if (CAN_MOVE(element) && !started_moving)
8163   {
8164     int move_pattern = element_info[element].move_pattern;
8165     int newx, newy;
8166
8167     Moving2Blocked(x, y, &newx, &newy);
8168
8169     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8170       return;
8171
8172     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8173         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8174     {
8175       WasJustMoving[x][y] = 0;
8176       CheckCollision[x][y] = 0;
8177
8178       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8179
8180       if (Tile[x][y] != element)        // element has changed
8181         return;
8182     }
8183
8184     if (!MovDelay[x][y])        // start new movement phase
8185     {
8186       // all objects that can change their move direction after each step
8187       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8188
8189       if (element != EL_YAMYAM &&
8190           element != EL_DARK_YAMYAM &&
8191           element != EL_PACMAN &&
8192           !(move_pattern & MV_ANY_DIRECTION) &&
8193           move_pattern != MV_TURNING_LEFT &&
8194           move_pattern != MV_TURNING_RIGHT &&
8195           move_pattern != MV_TURNING_LEFT_RIGHT &&
8196           move_pattern != MV_TURNING_RIGHT_LEFT &&
8197           move_pattern != MV_TURNING_RANDOM)
8198       {
8199         TurnRound(x, y);
8200
8201         if (MovDelay[x][y] && (element == EL_BUG ||
8202                                element == EL_SPACESHIP ||
8203                                element == EL_SP_SNIKSNAK ||
8204                                element == EL_SP_ELECTRON ||
8205                                element == EL_MOLE))
8206           TEST_DrawLevelField(x, y);
8207       }
8208     }
8209
8210     if (MovDelay[x][y])         // wait some time before next movement
8211     {
8212       MovDelay[x][y]--;
8213
8214       if (element == EL_ROBOT ||
8215           element == EL_YAMYAM ||
8216           element == EL_DARK_YAMYAM)
8217       {
8218         DrawLevelElementAnimationIfNeeded(x, y, element);
8219         PlayLevelSoundAction(x, y, ACTION_WAITING);
8220       }
8221       else if (element == EL_SP_ELECTRON)
8222         DrawLevelElementAnimationIfNeeded(x, y, element);
8223       else if (element == EL_DRAGON)
8224       {
8225         int i;
8226         int dir = MovDir[x][y];
8227         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8228         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8229         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8230                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8231                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8232                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8233         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8234
8235         GfxAction[x][y] = ACTION_ATTACKING;
8236
8237         if (IS_PLAYER(x, y))
8238           DrawPlayerField(x, y);
8239         else
8240           TEST_DrawLevelField(x, y);
8241
8242         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8243
8244         for (i = 1; i <= 3; i++)
8245         {
8246           int xx = x + i * dx;
8247           int yy = y + i * dy;
8248           int sx = SCREENX(xx);
8249           int sy = SCREENY(yy);
8250           int flame_graphic = graphic + (i - 1);
8251
8252           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8253             break;
8254
8255           if (MovDelay[x][y])
8256           {
8257             int flamed = MovingOrBlocked2Element(xx, yy);
8258
8259             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8260               Bang(xx, yy);
8261             else
8262               RemoveMovingField(xx, yy);
8263
8264             ChangeDelay[xx][yy] = 0;
8265
8266             Tile[xx][yy] = EL_FLAMES;
8267
8268             if (IN_SCR_FIELD(sx, sy))
8269             {
8270               TEST_DrawLevelFieldCrumbled(xx, yy);
8271               DrawGraphic(sx, sy, flame_graphic, frame);
8272             }
8273           }
8274           else
8275           {
8276             if (Tile[xx][yy] == EL_FLAMES)
8277               Tile[xx][yy] = EL_EMPTY;
8278             TEST_DrawLevelField(xx, yy);
8279           }
8280         }
8281       }
8282
8283       if (MovDelay[x][y])       // element still has to wait some time
8284       {
8285         PlayLevelSoundAction(x, y, ACTION_WAITING);
8286
8287         return;
8288       }
8289     }
8290
8291     // now make next step
8292
8293     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8294
8295     if (DONT_COLLIDE_WITH(element) &&
8296         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8297         !PLAYER_ENEMY_PROTECTED(newx, newy))
8298     {
8299       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8300
8301       return;
8302     }
8303
8304     else if (CAN_MOVE_INTO_ACID(element) &&
8305              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8306              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8307              (MovDir[x][y] == MV_DOWN ||
8308               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8309     {
8310       SplashAcid(newx, newy);
8311       Store[x][y] = EL_ACID;
8312     }
8313     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8314     {
8315       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8316           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8317           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8318           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8319       {
8320         RemoveField(x, y);
8321         TEST_DrawLevelField(x, y);
8322
8323         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8324         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8325           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8326
8327         game.friends_still_needed--;
8328         if (!game.friends_still_needed &&
8329             !game.GameOver &&
8330             game.all_players_gone)
8331           LevelSolved();
8332
8333         return;
8334       }
8335       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8336       {
8337         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8338           TEST_DrawLevelField(newx, newy);
8339         else
8340           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8341       }
8342       else if (!IS_FREE(newx, newy))
8343       {
8344         GfxAction[x][y] = ACTION_WAITING;
8345
8346         if (IS_PLAYER(x, y))
8347           DrawPlayerField(x, y);
8348         else
8349           TEST_DrawLevelField(x, y);
8350
8351         return;
8352       }
8353     }
8354     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8355     {
8356       if (IS_FOOD_PIG(Tile[newx][newy]))
8357       {
8358         if (IS_MOVING(newx, newy))
8359           RemoveMovingField(newx, newy);
8360         else
8361         {
8362           Tile[newx][newy] = EL_EMPTY;
8363           TEST_DrawLevelField(newx, newy);
8364         }
8365
8366         PlayLevelSound(x, y, SND_PIG_DIGGING);
8367       }
8368       else if (!IS_FREE(newx, newy))
8369       {
8370         if (IS_PLAYER(x, y))
8371           DrawPlayerField(x, y);
8372         else
8373           TEST_DrawLevelField(x, y);
8374
8375         return;
8376       }
8377     }
8378     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8379     {
8380       if (Store[x][y] != EL_EMPTY)
8381       {
8382         boolean can_clone = FALSE;
8383         int xx, yy;
8384
8385         // check if element to clone is still there
8386         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8387         {
8388           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8389           {
8390             can_clone = TRUE;
8391
8392             break;
8393           }
8394         }
8395
8396         // cannot clone or target field not free anymore -- do not clone
8397         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8398           Store[x][y] = EL_EMPTY;
8399       }
8400
8401       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8402       {
8403         if (IS_MV_DIAGONAL(MovDir[x][y]))
8404         {
8405           int diagonal_move_dir = MovDir[x][y];
8406           int stored = Store[x][y];
8407           int change_delay = 8;
8408           int graphic;
8409
8410           // android is moving diagonally
8411
8412           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8413
8414           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8415           GfxElement[x][y] = EL_EMC_ANDROID;
8416           GfxAction[x][y] = ACTION_SHRINKING;
8417           GfxDir[x][y] = diagonal_move_dir;
8418           ChangeDelay[x][y] = change_delay;
8419
8420           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8421                                    GfxDir[x][y]);
8422
8423           DrawLevelGraphicAnimation(x, y, graphic);
8424           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8425
8426           if (Tile[newx][newy] == EL_ACID)
8427           {
8428             SplashAcid(newx, newy);
8429
8430             return;
8431           }
8432
8433           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8434
8435           Store[newx][newy] = EL_EMC_ANDROID;
8436           GfxElement[newx][newy] = EL_EMC_ANDROID;
8437           GfxAction[newx][newy] = ACTION_GROWING;
8438           GfxDir[newx][newy] = diagonal_move_dir;
8439           ChangeDelay[newx][newy] = change_delay;
8440
8441           graphic = el_act_dir2img(GfxElement[newx][newy],
8442                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8443
8444           DrawLevelGraphicAnimation(newx, newy, graphic);
8445           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8446
8447           return;
8448         }
8449         else
8450         {
8451           Tile[newx][newy] = EL_EMPTY;
8452           TEST_DrawLevelField(newx, newy);
8453
8454           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8455         }
8456       }
8457       else if (!IS_FREE(newx, newy))
8458       {
8459         return;
8460       }
8461     }
8462     else if (IS_CUSTOM_ELEMENT(element) &&
8463              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8464     {
8465       if (!DigFieldByCE(newx, newy, element))
8466         return;
8467
8468       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8469       {
8470         RunnerVisit[x][y] = FrameCounter;
8471         PlayerVisit[x][y] /= 8;         // expire player visit path
8472       }
8473     }
8474     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8475     {
8476       if (!IS_FREE(newx, newy))
8477       {
8478         if (IS_PLAYER(x, y))
8479           DrawPlayerField(x, y);
8480         else
8481           TEST_DrawLevelField(x, y);
8482
8483         return;
8484       }
8485       else
8486       {
8487         boolean wanna_flame = !RND(10);
8488         int dx = newx - x, dy = newy - y;
8489         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8490         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8491         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8492                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8493         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8494                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8495
8496         if ((wanna_flame ||
8497              IS_CLASSIC_ENEMY(element1) ||
8498              IS_CLASSIC_ENEMY(element2)) &&
8499             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8500             element1 != EL_FLAMES && element2 != EL_FLAMES)
8501         {
8502           ResetGfxAnimation(x, y);
8503           GfxAction[x][y] = ACTION_ATTACKING;
8504
8505           if (IS_PLAYER(x, y))
8506             DrawPlayerField(x, y);
8507           else
8508             TEST_DrawLevelField(x, y);
8509
8510           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8511
8512           MovDelay[x][y] = 50;
8513
8514           Tile[newx][newy] = EL_FLAMES;
8515           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8516             Tile[newx1][newy1] = EL_FLAMES;
8517           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8518             Tile[newx2][newy2] = EL_FLAMES;
8519
8520           return;
8521         }
8522       }
8523     }
8524     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8525              Tile[newx][newy] == EL_DIAMOND)
8526     {
8527       if (IS_MOVING(newx, newy))
8528         RemoveMovingField(newx, newy);
8529       else
8530       {
8531         Tile[newx][newy] = EL_EMPTY;
8532         TEST_DrawLevelField(newx, newy);
8533       }
8534
8535       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8536     }
8537     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8538              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8539     {
8540       if (AmoebaNr[newx][newy])
8541       {
8542         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8543         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8544             Tile[newx][newy] == EL_BD_AMOEBA)
8545           AmoebaCnt[AmoebaNr[newx][newy]]--;
8546       }
8547
8548       if (IS_MOVING(newx, newy))
8549       {
8550         RemoveMovingField(newx, newy);
8551       }
8552       else
8553       {
8554         Tile[newx][newy] = EL_EMPTY;
8555         TEST_DrawLevelField(newx, newy);
8556       }
8557
8558       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8559     }
8560     else if ((element == EL_PACMAN || element == EL_MOLE)
8561              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8562     {
8563       if (AmoebaNr[newx][newy])
8564       {
8565         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8566         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8567             Tile[newx][newy] == EL_BD_AMOEBA)
8568           AmoebaCnt[AmoebaNr[newx][newy]]--;
8569       }
8570
8571       if (element == EL_MOLE)
8572       {
8573         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8574         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8575
8576         ResetGfxAnimation(x, y);
8577         GfxAction[x][y] = ACTION_DIGGING;
8578         TEST_DrawLevelField(x, y);
8579
8580         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8581
8582         return;                         // wait for shrinking amoeba
8583       }
8584       else      // element == EL_PACMAN
8585       {
8586         Tile[newx][newy] = EL_EMPTY;
8587         TEST_DrawLevelField(newx, newy);
8588         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8589       }
8590     }
8591     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8592              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8593               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8594     {
8595       // wait for shrinking amoeba to completely disappear
8596       return;
8597     }
8598     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8599     {
8600       // object was running against a wall
8601
8602       TurnRound(x, y);
8603
8604       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8605         DrawLevelElementAnimation(x, y, element);
8606
8607       if (DONT_TOUCH(element))
8608         TestIfBadThingTouchesPlayer(x, y);
8609
8610       return;
8611     }
8612
8613     InitMovingField(x, y, MovDir[x][y]);
8614
8615     PlayLevelSoundAction(x, y, ACTION_MOVING);
8616   }
8617
8618   if (MovDir[x][y])
8619     ContinueMoving(x, y);
8620 }
8621
8622 void ContinueMoving(int x, int y)
8623 {
8624   int element = Tile[x][y];
8625   struct ElementInfo *ei = &element_info[element];
8626   int direction = MovDir[x][y];
8627   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8628   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8629   int newx = x + dx, newy = y + dy;
8630   int stored = Store[x][y];
8631   int stored_new = Store[newx][newy];
8632   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8633   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8634   boolean last_line = (newy == lev_fieldy - 1);
8635   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8636
8637   if (pushed_by_player)         // special case: moving object pushed by player
8638   {
8639     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8640   }
8641   else if (use_step_delay)      // special case: moving object has step delay
8642   {
8643     if (!MovDelay[x][y])
8644       MovPos[x][y] += getElementMoveStepsize(x, y);
8645
8646     if (MovDelay[x][y])
8647       MovDelay[x][y]--;
8648     else
8649       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8650
8651     if (MovDelay[x][y])
8652     {
8653       TEST_DrawLevelField(x, y);
8654
8655       return;   // element is still waiting
8656     }
8657   }
8658   else                          // normal case: generically moving object
8659   {
8660     MovPos[x][y] += getElementMoveStepsize(x, y);
8661   }
8662
8663   if (ABS(MovPos[x][y]) < TILEX)
8664   {
8665     TEST_DrawLevelField(x, y);
8666
8667     return;     // element is still moving
8668   }
8669
8670   // element reached destination field
8671
8672   Tile[x][y] = EL_EMPTY;
8673   Tile[newx][newy] = element;
8674   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8675
8676   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8677   {
8678     element = Tile[newx][newy] = EL_ACID;
8679   }
8680   else if (element == EL_MOLE)
8681   {
8682     Tile[x][y] = EL_SAND;
8683
8684     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8685   }
8686   else if (element == EL_QUICKSAND_FILLING)
8687   {
8688     element = Tile[newx][newy] = get_next_element(element);
8689     Store[newx][newy] = Store[x][y];
8690   }
8691   else if (element == EL_QUICKSAND_EMPTYING)
8692   {
8693     Tile[x][y] = get_next_element(element);
8694     element = Tile[newx][newy] = Store[x][y];
8695   }
8696   else if (element == EL_QUICKSAND_FAST_FILLING)
8697   {
8698     element = Tile[newx][newy] = get_next_element(element);
8699     Store[newx][newy] = Store[x][y];
8700   }
8701   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8702   {
8703     Tile[x][y] = get_next_element(element);
8704     element = Tile[newx][newy] = Store[x][y];
8705   }
8706   else if (element == EL_MAGIC_WALL_FILLING)
8707   {
8708     element = Tile[newx][newy] = get_next_element(element);
8709     if (!game.magic_wall_active)
8710       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8711     Store[newx][newy] = Store[x][y];
8712   }
8713   else if (element == EL_MAGIC_WALL_EMPTYING)
8714   {
8715     Tile[x][y] = get_next_element(element);
8716     if (!game.magic_wall_active)
8717       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8718     element = Tile[newx][newy] = Store[x][y];
8719
8720     InitField(newx, newy, FALSE);
8721   }
8722   else if (element == EL_BD_MAGIC_WALL_FILLING)
8723   {
8724     element = Tile[newx][newy] = get_next_element(element);
8725     if (!game.magic_wall_active)
8726       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8727     Store[newx][newy] = Store[x][y];
8728   }
8729   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8730   {
8731     Tile[x][y] = get_next_element(element);
8732     if (!game.magic_wall_active)
8733       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8734     element = Tile[newx][newy] = Store[x][y];
8735
8736     InitField(newx, newy, FALSE);
8737   }
8738   else if (element == EL_DC_MAGIC_WALL_FILLING)
8739   {
8740     element = Tile[newx][newy] = get_next_element(element);
8741     if (!game.magic_wall_active)
8742       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8743     Store[newx][newy] = Store[x][y];
8744   }
8745   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8746   {
8747     Tile[x][y] = get_next_element(element);
8748     if (!game.magic_wall_active)
8749       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8750     element = Tile[newx][newy] = Store[x][y];
8751
8752     InitField(newx, newy, FALSE);
8753   }
8754   else if (element == EL_AMOEBA_DROPPING)
8755   {
8756     Tile[x][y] = get_next_element(element);
8757     element = Tile[newx][newy] = Store[x][y];
8758   }
8759   else if (element == EL_SOKOBAN_OBJECT)
8760   {
8761     if (Back[x][y])
8762       Tile[x][y] = Back[x][y];
8763
8764     if (Back[newx][newy])
8765       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8766
8767     Back[x][y] = Back[newx][newy] = 0;
8768   }
8769
8770   Store[x][y] = EL_EMPTY;
8771   MovPos[x][y] = 0;
8772   MovDir[x][y] = 0;
8773   MovDelay[x][y] = 0;
8774
8775   MovDelay[newx][newy] = 0;
8776
8777   if (CAN_CHANGE_OR_HAS_ACTION(element))
8778   {
8779     // copy element change control values to new field
8780     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8781     ChangePage[newx][newy]  = ChangePage[x][y];
8782     ChangeCount[newx][newy] = ChangeCount[x][y];
8783     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8784   }
8785
8786   CustomValue[newx][newy] = CustomValue[x][y];
8787
8788   ChangeDelay[x][y] = 0;
8789   ChangePage[x][y] = -1;
8790   ChangeCount[x][y] = 0;
8791   ChangeEvent[x][y] = -1;
8792
8793   CustomValue[x][y] = 0;
8794
8795   // copy animation control values to new field
8796   GfxFrame[newx][newy]  = GfxFrame[x][y];
8797   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8798   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8799   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8800
8801   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8802
8803   // some elements can leave other elements behind after moving
8804   if (ei->move_leave_element != EL_EMPTY &&
8805       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8806       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8807   {
8808     int move_leave_element = ei->move_leave_element;
8809
8810     // this makes it possible to leave the removed element again
8811     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8812       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8813
8814     Tile[x][y] = move_leave_element;
8815
8816     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8817       MovDir[x][y] = direction;
8818
8819     InitField(x, y, FALSE);
8820
8821     if (GFX_CRUMBLED(Tile[x][y]))
8822       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8823
8824     if (IS_PLAYER_ELEMENT(move_leave_element))
8825       RelocatePlayer(x, y, move_leave_element);
8826   }
8827
8828   // do this after checking for left-behind element
8829   ResetGfxAnimation(x, y);      // reset animation values for old field
8830
8831   if (!CAN_MOVE(element) ||
8832       (CAN_FALL(element) && direction == MV_DOWN &&
8833        (element == EL_SPRING ||
8834         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8835         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8836     GfxDir[x][y] = MovDir[newx][newy] = 0;
8837
8838   TEST_DrawLevelField(x, y);
8839   TEST_DrawLevelField(newx, newy);
8840
8841   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8842
8843   // prevent pushed element from moving on in pushed direction
8844   if (pushed_by_player && CAN_MOVE(element) &&
8845       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8846       !(element_info[element].move_pattern & direction))
8847     TurnRound(newx, newy);
8848
8849   // prevent elements on conveyor belt from moving on in last direction
8850   if (pushed_by_conveyor && CAN_FALL(element) &&
8851       direction & MV_HORIZONTAL)
8852     MovDir[newx][newy] = 0;
8853
8854   if (!pushed_by_player)
8855   {
8856     int nextx = newx + dx, nexty = newy + dy;
8857     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8858
8859     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8860
8861     if (CAN_FALL(element) && direction == MV_DOWN)
8862       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8863
8864     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8865       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8866
8867     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8868       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8869   }
8870
8871   if (DONT_TOUCH(element))      // object may be nasty to player or others
8872   {
8873     TestIfBadThingTouchesPlayer(newx, newy);
8874     TestIfBadThingTouchesFriend(newx, newy);
8875
8876     if (!IS_CUSTOM_ELEMENT(element))
8877       TestIfBadThingTouchesOtherBadThing(newx, newy);
8878   }
8879   else if (element == EL_PENGUIN)
8880     TestIfFriendTouchesBadThing(newx, newy);
8881
8882   if (DONT_GET_HIT_BY(element))
8883   {
8884     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8885   }
8886
8887   // give the player one last chance (one more frame) to move away
8888   if (CAN_FALL(element) && direction == MV_DOWN &&
8889       (last_line || (!IS_FREE(x, newy + 1) &&
8890                      (!IS_PLAYER(x, newy + 1) ||
8891                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8892     Impact(x, newy);
8893
8894   if (pushed_by_player && !game.use_change_when_pushing_bug)
8895   {
8896     int push_side = MV_DIR_OPPOSITE(direction);
8897     struct PlayerInfo *player = PLAYERINFO(x, y);
8898
8899     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8900                                player->index_bit, push_side);
8901     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8902                                         player->index_bit, push_side);
8903   }
8904
8905   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8906     MovDelay[newx][newy] = 1;
8907
8908   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8909
8910   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8911   TestIfElementHitsCustomElement(newx, newy, direction);
8912   TestIfPlayerTouchesCustomElement(newx, newy);
8913   TestIfElementTouchesCustomElement(newx, newy);
8914
8915   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8916       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8917     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8918                              MV_DIR_OPPOSITE(direction));
8919 }
8920
8921 int AmoebaNeighbourNr(int ax, int ay)
8922 {
8923   int i;
8924   int element = Tile[ax][ay];
8925   int group_nr = 0;
8926   static int xy[4][2] =
8927   {
8928     { 0, -1 },
8929     { -1, 0 },
8930     { +1, 0 },
8931     { 0, +1 }
8932   };
8933
8934   for (i = 0; i < NUM_DIRECTIONS; i++)
8935   {
8936     int x = ax + xy[i][0];
8937     int y = ay + xy[i][1];
8938
8939     if (!IN_LEV_FIELD(x, y))
8940       continue;
8941
8942     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8943       group_nr = AmoebaNr[x][y];
8944   }
8945
8946   return group_nr;
8947 }
8948
8949 static void AmoebaMerge(int ax, int ay)
8950 {
8951   int i, x, y, xx, yy;
8952   int new_group_nr = AmoebaNr[ax][ay];
8953   static int xy[4][2] =
8954   {
8955     { 0, -1 },
8956     { -1, 0 },
8957     { +1, 0 },
8958     { 0, +1 }
8959   };
8960
8961   if (new_group_nr == 0)
8962     return;
8963
8964   for (i = 0; i < NUM_DIRECTIONS; i++)
8965   {
8966     x = ax + xy[i][0];
8967     y = ay + xy[i][1];
8968
8969     if (!IN_LEV_FIELD(x, y))
8970       continue;
8971
8972     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8973          Tile[x][y] == EL_BD_AMOEBA ||
8974          Tile[x][y] == EL_AMOEBA_DEAD) &&
8975         AmoebaNr[x][y] != new_group_nr)
8976     {
8977       int old_group_nr = AmoebaNr[x][y];
8978
8979       if (old_group_nr == 0)
8980         return;
8981
8982       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8983       AmoebaCnt[old_group_nr] = 0;
8984       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8985       AmoebaCnt2[old_group_nr] = 0;
8986
8987       SCAN_PLAYFIELD(xx, yy)
8988       {
8989         if (AmoebaNr[xx][yy] == old_group_nr)
8990           AmoebaNr[xx][yy] = new_group_nr;
8991       }
8992     }
8993   }
8994 }
8995
8996 void AmoebaToDiamond(int ax, int ay)
8997 {
8998   int i, x, y;
8999
9000   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9001   {
9002     int group_nr = AmoebaNr[ax][ay];
9003
9004 #ifdef DEBUG
9005     if (group_nr == 0)
9006     {
9007       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9008       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9009
9010       return;
9011     }
9012 #endif
9013
9014     SCAN_PLAYFIELD(x, y)
9015     {
9016       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9017       {
9018         AmoebaNr[x][y] = 0;
9019         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9020       }
9021     }
9022
9023     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9024                             SND_AMOEBA_TURNING_TO_GEM :
9025                             SND_AMOEBA_TURNING_TO_ROCK));
9026     Bang(ax, ay);
9027   }
9028   else
9029   {
9030     static int xy[4][2] =
9031     {
9032       { 0, -1 },
9033       { -1, 0 },
9034       { +1, 0 },
9035       { 0, +1 }
9036     };
9037
9038     for (i = 0; i < NUM_DIRECTIONS; i++)
9039     {
9040       x = ax + xy[i][0];
9041       y = ay + xy[i][1];
9042
9043       if (!IN_LEV_FIELD(x, y))
9044         continue;
9045
9046       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9047       {
9048         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9049                               SND_AMOEBA_TURNING_TO_GEM :
9050                               SND_AMOEBA_TURNING_TO_ROCK));
9051         Bang(x, y);
9052       }
9053     }
9054   }
9055 }
9056
9057 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9058 {
9059   int x, y;
9060   int group_nr = AmoebaNr[ax][ay];
9061   boolean done = FALSE;
9062
9063 #ifdef DEBUG
9064   if (group_nr == 0)
9065   {
9066     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9067     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9068
9069     return;
9070   }
9071 #endif
9072
9073   SCAN_PLAYFIELD(x, y)
9074   {
9075     if (AmoebaNr[x][y] == group_nr &&
9076         (Tile[x][y] == EL_AMOEBA_DEAD ||
9077          Tile[x][y] == EL_BD_AMOEBA ||
9078          Tile[x][y] == EL_AMOEBA_GROWING))
9079     {
9080       AmoebaNr[x][y] = 0;
9081       Tile[x][y] = new_element;
9082       InitField(x, y, FALSE);
9083       TEST_DrawLevelField(x, y);
9084       done = TRUE;
9085     }
9086   }
9087
9088   if (done)
9089     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9090                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9091                             SND_BD_AMOEBA_TURNING_TO_GEM));
9092 }
9093
9094 static void AmoebaGrowing(int x, int y)
9095 {
9096   static unsigned int sound_delay = 0;
9097   static unsigned int sound_delay_value = 0;
9098
9099   if (!MovDelay[x][y])          // start new growing cycle
9100   {
9101     MovDelay[x][y] = 7;
9102
9103     if (DelayReached(&sound_delay, sound_delay_value))
9104     {
9105       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9106       sound_delay_value = 30;
9107     }
9108   }
9109
9110   if (MovDelay[x][y])           // wait some time before growing bigger
9111   {
9112     MovDelay[x][y]--;
9113     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9114     {
9115       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9116                                            6 - MovDelay[x][y]);
9117
9118       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9119     }
9120
9121     if (!MovDelay[x][y])
9122     {
9123       Tile[x][y] = Store[x][y];
9124       Store[x][y] = 0;
9125       TEST_DrawLevelField(x, y);
9126     }
9127   }
9128 }
9129
9130 static void AmoebaShrinking(int x, int y)
9131 {
9132   static unsigned int sound_delay = 0;
9133   static unsigned int sound_delay_value = 0;
9134
9135   if (!MovDelay[x][y])          // start new shrinking cycle
9136   {
9137     MovDelay[x][y] = 7;
9138
9139     if (DelayReached(&sound_delay, sound_delay_value))
9140       sound_delay_value = 30;
9141   }
9142
9143   if (MovDelay[x][y])           // wait some time before shrinking
9144   {
9145     MovDelay[x][y]--;
9146     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9147     {
9148       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9149                                            6 - MovDelay[x][y]);
9150
9151       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9152     }
9153
9154     if (!MovDelay[x][y])
9155     {
9156       Tile[x][y] = EL_EMPTY;
9157       TEST_DrawLevelField(x, y);
9158
9159       // don't let mole enter this field in this cycle;
9160       // (give priority to objects falling to this field from above)
9161       Stop[x][y] = TRUE;
9162     }
9163   }
9164 }
9165
9166 static void AmoebaReproduce(int ax, int ay)
9167 {
9168   int i;
9169   int element = Tile[ax][ay];
9170   int graphic = el2img(element);
9171   int newax = ax, neway = ay;
9172   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9173   static int xy[4][2] =
9174   {
9175     { 0, -1 },
9176     { -1, 0 },
9177     { +1, 0 },
9178     { 0, +1 }
9179   };
9180
9181   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9182   {
9183     Tile[ax][ay] = EL_AMOEBA_DEAD;
9184     TEST_DrawLevelField(ax, ay);
9185     return;
9186   }
9187
9188   if (IS_ANIMATED(graphic))
9189     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9190
9191   if (!MovDelay[ax][ay])        // start making new amoeba field
9192     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9193
9194   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9195   {
9196     MovDelay[ax][ay]--;
9197     if (MovDelay[ax][ay])
9198       return;
9199   }
9200
9201   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9202   {
9203     int start = RND(4);
9204     int x = ax + xy[start][0];
9205     int y = ay + xy[start][1];
9206
9207     if (!IN_LEV_FIELD(x, y))
9208       return;
9209
9210     if (IS_FREE(x, y) ||
9211         CAN_GROW_INTO(Tile[x][y]) ||
9212         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9213         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9214     {
9215       newax = x;
9216       neway = y;
9217     }
9218
9219     if (newax == ax && neway == ay)
9220       return;
9221   }
9222   else                          // normal or "filled" (BD style) amoeba
9223   {
9224     int start = RND(4);
9225     boolean waiting_for_player = FALSE;
9226
9227     for (i = 0; i < NUM_DIRECTIONS; i++)
9228     {
9229       int j = (start + i) % 4;
9230       int x = ax + xy[j][0];
9231       int y = ay + xy[j][1];
9232
9233       if (!IN_LEV_FIELD(x, y))
9234         continue;
9235
9236       if (IS_FREE(x, y) ||
9237           CAN_GROW_INTO(Tile[x][y]) ||
9238           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9239           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9240       {
9241         newax = x;
9242         neway = y;
9243         break;
9244       }
9245       else if (IS_PLAYER(x, y))
9246         waiting_for_player = TRUE;
9247     }
9248
9249     if (newax == ax && neway == ay)             // amoeba cannot grow
9250     {
9251       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9252       {
9253         Tile[ax][ay] = EL_AMOEBA_DEAD;
9254         TEST_DrawLevelField(ax, ay);
9255         AmoebaCnt[AmoebaNr[ax][ay]]--;
9256
9257         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9258         {
9259           if (element == EL_AMOEBA_FULL)
9260             AmoebaToDiamond(ax, ay);
9261           else if (element == EL_BD_AMOEBA)
9262             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9263         }
9264       }
9265       return;
9266     }
9267     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9268     {
9269       // amoeba gets larger by growing in some direction
9270
9271       int new_group_nr = AmoebaNr[ax][ay];
9272
9273 #ifdef DEBUG
9274   if (new_group_nr == 0)
9275   {
9276     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9277           newax, neway);
9278     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9279
9280     return;
9281   }
9282 #endif
9283
9284       AmoebaNr[newax][neway] = new_group_nr;
9285       AmoebaCnt[new_group_nr]++;
9286       AmoebaCnt2[new_group_nr]++;
9287
9288       // if amoeba touches other amoeba(s) after growing, unify them
9289       AmoebaMerge(newax, neway);
9290
9291       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9292       {
9293         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9294         return;
9295       }
9296     }
9297   }
9298
9299   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9300       (neway == lev_fieldy - 1 && newax != ax))
9301   {
9302     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9303     Store[newax][neway] = element;
9304   }
9305   else if (neway == ay || element == EL_EMC_DRIPPER)
9306   {
9307     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9308
9309     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9310   }
9311   else
9312   {
9313     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9314     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9315     Store[ax][ay] = EL_AMOEBA_DROP;
9316     ContinueMoving(ax, ay);
9317     return;
9318   }
9319
9320   TEST_DrawLevelField(newax, neway);
9321 }
9322
9323 static void Life(int ax, int ay)
9324 {
9325   int x1, y1, x2, y2;
9326   int life_time = 40;
9327   int element = Tile[ax][ay];
9328   int graphic = el2img(element);
9329   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9330                          level.biomaze);
9331   boolean changed = FALSE;
9332
9333   if (IS_ANIMATED(graphic))
9334     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9335
9336   if (Stop[ax][ay])
9337     return;
9338
9339   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9340     MovDelay[ax][ay] = life_time;
9341
9342   if (MovDelay[ax][ay])         // wait some time before next cycle
9343   {
9344     MovDelay[ax][ay]--;
9345     if (MovDelay[ax][ay])
9346       return;
9347   }
9348
9349   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9350   {
9351     int xx = ax+x1, yy = ay+y1;
9352     int old_element = Tile[xx][yy];
9353     int num_neighbours = 0;
9354
9355     if (!IN_LEV_FIELD(xx, yy))
9356       continue;
9357
9358     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9359     {
9360       int x = xx+x2, y = yy+y2;
9361
9362       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9363         continue;
9364
9365       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9366       boolean is_neighbour = FALSE;
9367
9368       if (level.use_life_bugs)
9369         is_neighbour =
9370           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9371            (IS_FREE(x, y)                             &&  Stop[x][y]));
9372       else
9373         is_neighbour =
9374           (Last[x][y] == element || is_player_cell);
9375
9376       if (is_neighbour)
9377         num_neighbours++;
9378     }
9379
9380     boolean is_free = FALSE;
9381
9382     if (level.use_life_bugs)
9383       is_free = (IS_FREE(xx, yy));
9384     else
9385       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9386
9387     if (xx == ax && yy == ay)           // field in the middle
9388     {
9389       if (num_neighbours < life_parameter[0] ||
9390           num_neighbours > life_parameter[1])
9391       {
9392         Tile[xx][yy] = EL_EMPTY;
9393         if (Tile[xx][yy] != old_element)
9394           TEST_DrawLevelField(xx, yy);
9395         Stop[xx][yy] = TRUE;
9396         changed = TRUE;
9397       }
9398     }
9399     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9400     {                                   // free border field
9401       if (num_neighbours >= life_parameter[2] &&
9402           num_neighbours <= life_parameter[3])
9403       {
9404         Tile[xx][yy] = element;
9405         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9406         if (Tile[xx][yy] != old_element)
9407           TEST_DrawLevelField(xx, yy);
9408         Stop[xx][yy] = TRUE;
9409         changed = TRUE;
9410       }
9411     }
9412   }
9413
9414   if (changed)
9415     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9416                    SND_GAME_OF_LIFE_GROWING);
9417 }
9418
9419 static void InitRobotWheel(int x, int y)
9420 {
9421   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9422 }
9423
9424 static void RunRobotWheel(int x, int y)
9425 {
9426   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9427 }
9428
9429 static void StopRobotWheel(int x, int y)
9430 {
9431   if (game.robot_wheel_x == x &&
9432       game.robot_wheel_y == y)
9433   {
9434     game.robot_wheel_x = -1;
9435     game.robot_wheel_y = -1;
9436     game.robot_wheel_active = FALSE;
9437   }
9438 }
9439
9440 static void InitTimegateWheel(int x, int y)
9441 {
9442   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9443 }
9444
9445 static void RunTimegateWheel(int x, int y)
9446 {
9447   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9448 }
9449
9450 static void InitMagicBallDelay(int x, int y)
9451 {
9452   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9453 }
9454
9455 static void ActivateMagicBall(int bx, int by)
9456 {
9457   int x, y;
9458
9459   if (level.ball_random)
9460   {
9461     int pos_border = RND(8);    // select one of the eight border elements
9462     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9463     int xx = pos_content % 3;
9464     int yy = pos_content / 3;
9465
9466     x = bx - 1 + xx;
9467     y = by - 1 + yy;
9468
9469     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9470       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9471   }
9472   else
9473   {
9474     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9475     {
9476       int xx = x - bx + 1;
9477       int yy = y - by + 1;
9478
9479       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9480         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9481     }
9482   }
9483
9484   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9485 }
9486
9487 static void CheckExit(int x, int y)
9488 {
9489   if (game.gems_still_needed > 0 ||
9490       game.sokoban_fields_still_needed > 0 ||
9491       game.sokoban_objects_still_needed > 0 ||
9492       game.lights_still_needed > 0)
9493   {
9494     int element = Tile[x][y];
9495     int graphic = el2img(element);
9496
9497     if (IS_ANIMATED(graphic))
9498       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9499
9500     return;
9501   }
9502
9503   // do not re-open exit door closed after last player
9504   if (game.all_players_gone)
9505     return;
9506
9507   Tile[x][y] = EL_EXIT_OPENING;
9508
9509   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9510 }
9511
9512 static void CheckExitEM(int x, int y)
9513 {
9514   if (game.gems_still_needed > 0 ||
9515       game.sokoban_fields_still_needed > 0 ||
9516       game.sokoban_objects_still_needed > 0 ||
9517       game.lights_still_needed > 0)
9518   {
9519     int element = Tile[x][y];
9520     int graphic = el2img(element);
9521
9522     if (IS_ANIMATED(graphic))
9523       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9524
9525     return;
9526   }
9527
9528   // do not re-open exit door closed after last player
9529   if (game.all_players_gone)
9530     return;
9531
9532   Tile[x][y] = EL_EM_EXIT_OPENING;
9533
9534   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9535 }
9536
9537 static void CheckExitSteel(int x, int y)
9538 {
9539   if (game.gems_still_needed > 0 ||
9540       game.sokoban_fields_still_needed > 0 ||
9541       game.sokoban_objects_still_needed > 0 ||
9542       game.lights_still_needed > 0)
9543   {
9544     int element = Tile[x][y];
9545     int graphic = el2img(element);
9546
9547     if (IS_ANIMATED(graphic))
9548       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9549
9550     return;
9551   }
9552
9553   // do not re-open exit door closed after last player
9554   if (game.all_players_gone)
9555     return;
9556
9557   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9558
9559   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9560 }
9561
9562 static void CheckExitSteelEM(int x, int y)
9563 {
9564   if (game.gems_still_needed > 0 ||
9565       game.sokoban_fields_still_needed > 0 ||
9566       game.sokoban_objects_still_needed > 0 ||
9567       game.lights_still_needed > 0)
9568   {
9569     int element = Tile[x][y];
9570     int graphic = el2img(element);
9571
9572     if (IS_ANIMATED(graphic))
9573       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9574
9575     return;
9576   }
9577
9578   // do not re-open exit door closed after last player
9579   if (game.all_players_gone)
9580     return;
9581
9582   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9583
9584   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9585 }
9586
9587 static void CheckExitSP(int x, int y)
9588 {
9589   if (game.gems_still_needed > 0)
9590   {
9591     int element = Tile[x][y];
9592     int graphic = el2img(element);
9593
9594     if (IS_ANIMATED(graphic))
9595       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9596
9597     return;
9598   }
9599
9600   // do not re-open exit door closed after last player
9601   if (game.all_players_gone)
9602     return;
9603
9604   Tile[x][y] = EL_SP_EXIT_OPENING;
9605
9606   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9607 }
9608
9609 static void CloseAllOpenTimegates(void)
9610 {
9611   int x, y;
9612
9613   SCAN_PLAYFIELD(x, y)
9614   {
9615     int element = Tile[x][y];
9616
9617     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9618     {
9619       Tile[x][y] = EL_TIMEGATE_CLOSING;
9620
9621       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9622     }
9623   }
9624 }
9625
9626 static void DrawTwinkleOnField(int x, int y)
9627 {
9628   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9629     return;
9630
9631   if (Tile[x][y] == EL_BD_DIAMOND)
9632     return;
9633
9634   if (MovDelay[x][y] == 0)      // next animation frame
9635     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9636
9637   if (MovDelay[x][y] != 0)      // wait some time before next frame
9638   {
9639     MovDelay[x][y]--;
9640
9641     DrawLevelElementAnimation(x, y, Tile[x][y]);
9642
9643     if (MovDelay[x][y] != 0)
9644     {
9645       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9646                                            10 - MovDelay[x][y]);
9647
9648       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9649     }
9650   }
9651 }
9652
9653 static void MauerWaechst(int x, int y)
9654 {
9655   int delay = 6;
9656
9657   if (!MovDelay[x][y])          // next animation frame
9658     MovDelay[x][y] = 3 * delay;
9659
9660   if (MovDelay[x][y])           // wait some time before next frame
9661   {
9662     MovDelay[x][y]--;
9663
9664     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9665     {
9666       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9667       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9668
9669       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9670     }
9671
9672     if (!MovDelay[x][y])
9673     {
9674       if (MovDir[x][y] == MV_LEFT)
9675       {
9676         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9677           TEST_DrawLevelField(x - 1, y);
9678       }
9679       else if (MovDir[x][y] == MV_RIGHT)
9680       {
9681         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9682           TEST_DrawLevelField(x + 1, y);
9683       }
9684       else if (MovDir[x][y] == MV_UP)
9685       {
9686         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9687           TEST_DrawLevelField(x, y - 1);
9688       }
9689       else
9690       {
9691         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9692           TEST_DrawLevelField(x, y + 1);
9693       }
9694
9695       Tile[x][y] = Store[x][y];
9696       Store[x][y] = 0;
9697       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9698       TEST_DrawLevelField(x, y);
9699     }
9700   }
9701 }
9702
9703 static void MauerAbleger(int ax, int ay)
9704 {
9705   int element = Tile[ax][ay];
9706   int graphic = el2img(element);
9707   boolean oben_frei = FALSE, unten_frei = FALSE;
9708   boolean links_frei = FALSE, rechts_frei = FALSE;
9709   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9710   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9711   boolean new_wall = FALSE;
9712
9713   if (IS_ANIMATED(graphic))
9714     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9715
9716   if (!MovDelay[ax][ay])        // start building new wall
9717     MovDelay[ax][ay] = 6;
9718
9719   if (MovDelay[ax][ay])         // wait some time before building new wall
9720   {
9721     MovDelay[ax][ay]--;
9722     if (MovDelay[ax][ay])
9723       return;
9724   }
9725
9726   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9727     oben_frei = TRUE;
9728   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9729     unten_frei = TRUE;
9730   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9731     links_frei = TRUE;
9732   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9733     rechts_frei = TRUE;
9734
9735   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9736       element == EL_EXPANDABLE_WALL_ANY)
9737   {
9738     if (oben_frei)
9739     {
9740       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9741       Store[ax][ay-1] = element;
9742       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9743       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9744         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9745                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9746       new_wall = TRUE;
9747     }
9748     if (unten_frei)
9749     {
9750       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9751       Store[ax][ay+1] = element;
9752       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9753       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9754         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9755                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9756       new_wall = TRUE;
9757     }
9758   }
9759
9760   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9761       element == EL_EXPANDABLE_WALL_ANY ||
9762       element == EL_EXPANDABLE_WALL ||
9763       element == EL_BD_EXPANDABLE_WALL)
9764   {
9765     if (links_frei)
9766     {
9767       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9768       Store[ax-1][ay] = element;
9769       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9770       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9771         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9772                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9773       new_wall = TRUE;
9774     }
9775
9776     if (rechts_frei)
9777     {
9778       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9779       Store[ax+1][ay] = element;
9780       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9781       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9782         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9783                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9784       new_wall = TRUE;
9785     }
9786   }
9787
9788   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9789     TEST_DrawLevelField(ax, ay);
9790
9791   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9792     oben_massiv = TRUE;
9793   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9794     unten_massiv = TRUE;
9795   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9796     links_massiv = TRUE;
9797   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9798     rechts_massiv = TRUE;
9799
9800   if (((oben_massiv && unten_massiv) ||
9801        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9802        element == EL_EXPANDABLE_WALL) &&
9803       ((links_massiv && rechts_massiv) ||
9804        element == EL_EXPANDABLE_WALL_VERTICAL))
9805     Tile[ax][ay] = EL_WALL;
9806
9807   if (new_wall)
9808     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9809 }
9810
9811 static void MauerAblegerStahl(int ax, int ay)
9812 {
9813   int element = Tile[ax][ay];
9814   int graphic = el2img(element);
9815   boolean oben_frei = FALSE, unten_frei = FALSE;
9816   boolean links_frei = FALSE, rechts_frei = FALSE;
9817   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9818   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9819   boolean new_wall = FALSE;
9820
9821   if (IS_ANIMATED(graphic))
9822     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9823
9824   if (!MovDelay[ax][ay])        // start building new wall
9825     MovDelay[ax][ay] = 6;
9826
9827   if (MovDelay[ax][ay])         // wait some time before building new wall
9828   {
9829     MovDelay[ax][ay]--;
9830     if (MovDelay[ax][ay])
9831       return;
9832   }
9833
9834   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9835     oben_frei = TRUE;
9836   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9837     unten_frei = TRUE;
9838   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9839     links_frei = TRUE;
9840   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9841     rechts_frei = TRUE;
9842
9843   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9844       element == EL_EXPANDABLE_STEELWALL_ANY)
9845   {
9846     if (oben_frei)
9847     {
9848       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9849       Store[ax][ay-1] = element;
9850       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9851       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9852         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9853                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9854       new_wall = TRUE;
9855     }
9856     if (unten_frei)
9857     {
9858       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9859       Store[ax][ay+1] = element;
9860       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9861       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9862         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9863                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9864       new_wall = TRUE;
9865     }
9866   }
9867
9868   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9869       element == EL_EXPANDABLE_STEELWALL_ANY)
9870   {
9871     if (links_frei)
9872     {
9873       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9874       Store[ax-1][ay] = element;
9875       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9876       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9877         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9878                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9879       new_wall = TRUE;
9880     }
9881
9882     if (rechts_frei)
9883     {
9884       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9885       Store[ax+1][ay] = element;
9886       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9887       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9888         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9889                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9890       new_wall = TRUE;
9891     }
9892   }
9893
9894   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9895     oben_massiv = TRUE;
9896   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9897     unten_massiv = TRUE;
9898   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9899     links_massiv = TRUE;
9900   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9901     rechts_massiv = TRUE;
9902
9903   if (((oben_massiv && unten_massiv) ||
9904        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9905       ((links_massiv && rechts_massiv) ||
9906        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9907     Tile[ax][ay] = EL_STEELWALL;
9908
9909   if (new_wall)
9910     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9911 }
9912
9913 static void CheckForDragon(int x, int y)
9914 {
9915   int i, j;
9916   boolean dragon_found = FALSE;
9917   static int xy[4][2] =
9918   {
9919     { 0, -1 },
9920     { -1, 0 },
9921     { +1, 0 },
9922     { 0, +1 }
9923   };
9924
9925   for (i = 0; i < NUM_DIRECTIONS; i++)
9926   {
9927     for (j = 0; j < 4; j++)
9928     {
9929       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9930
9931       if (IN_LEV_FIELD(xx, yy) &&
9932           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9933       {
9934         if (Tile[xx][yy] == EL_DRAGON)
9935           dragon_found = TRUE;
9936       }
9937       else
9938         break;
9939     }
9940   }
9941
9942   if (!dragon_found)
9943   {
9944     for (i = 0; i < NUM_DIRECTIONS; i++)
9945     {
9946       for (j = 0; j < 3; j++)
9947       {
9948         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9949   
9950         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9951         {
9952           Tile[xx][yy] = EL_EMPTY;
9953           TEST_DrawLevelField(xx, yy);
9954         }
9955         else
9956           break;
9957       }
9958     }
9959   }
9960 }
9961
9962 static void InitBuggyBase(int x, int y)
9963 {
9964   int element = Tile[x][y];
9965   int activating_delay = FRAMES_PER_SECOND / 4;
9966
9967   ChangeDelay[x][y] =
9968     (element == EL_SP_BUGGY_BASE ?
9969      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9970      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9971      activating_delay :
9972      element == EL_SP_BUGGY_BASE_ACTIVE ?
9973      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9974 }
9975
9976 static void WarnBuggyBase(int x, int y)
9977 {
9978   int i;
9979   static int xy[4][2] =
9980   {
9981     { 0, -1 },
9982     { -1, 0 },
9983     { +1, 0 },
9984     { 0, +1 }
9985   };
9986
9987   for (i = 0; i < NUM_DIRECTIONS; i++)
9988   {
9989     int xx = x + xy[i][0];
9990     int yy = y + xy[i][1];
9991
9992     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9993     {
9994       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9995
9996       break;
9997     }
9998   }
9999 }
10000
10001 static void InitTrap(int x, int y)
10002 {
10003   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10004 }
10005
10006 static void ActivateTrap(int x, int y)
10007 {
10008   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10009 }
10010
10011 static void ChangeActiveTrap(int x, int y)
10012 {
10013   int graphic = IMG_TRAP_ACTIVE;
10014
10015   // if new animation frame was drawn, correct crumbled sand border
10016   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10017     TEST_DrawLevelFieldCrumbled(x, y);
10018 }
10019
10020 static int getSpecialActionElement(int element, int number, int base_element)
10021 {
10022   return (element != EL_EMPTY ? element :
10023           number != -1 ? base_element + number - 1 :
10024           EL_EMPTY);
10025 }
10026
10027 static int getModifiedActionNumber(int value_old, int operator, int operand,
10028                                    int value_min, int value_max)
10029 {
10030   int value_new = (operator == CA_MODE_SET      ? operand :
10031                    operator == CA_MODE_ADD      ? value_old + operand :
10032                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10033                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10034                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10035                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10036                    value_old);
10037
10038   return (value_new < value_min ? value_min :
10039           value_new > value_max ? value_max :
10040           value_new);
10041 }
10042
10043 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10044 {
10045   struct ElementInfo *ei = &element_info[element];
10046   struct ElementChangeInfo *change = &ei->change_page[page];
10047   int target_element = change->target_element;
10048   int action_type = change->action_type;
10049   int action_mode = change->action_mode;
10050   int action_arg = change->action_arg;
10051   int action_element = change->action_element;
10052   int i;
10053
10054   if (!change->has_action)
10055     return;
10056
10057   // ---------- determine action paramater values -----------------------------
10058
10059   int level_time_value =
10060     (level.time > 0 ? TimeLeft :
10061      TimePlayed);
10062
10063   int action_arg_element_raw =
10064     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10065      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10066      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10067      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10068      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10069      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10070      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10071      EL_EMPTY);
10072   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10073
10074   int action_arg_direction =
10075     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10076      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10077      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10078      change->actual_trigger_side :
10079      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10080      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10081      MV_NONE);
10082
10083   int action_arg_number_min =
10084     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10085      CA_ARG_MIN);
10086
10087   int action_arg_number_max =
10088     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10089      action_type == CA_SET_LEVEL_GEMS ? 999 :
10090      action_type == CA_SET_LEVEL_TIME ? 9999 :
10091      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10092      action_type == CA_SET_CE_VALUE ? 9999 :
10093      action_type == CA_SET_CE_SCORE ? 9999 :
10094      CA_ARG_MAX);
10095
10096   int action_arg_number_reset =
10097     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10098      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10099      action_type == CA_SET_LEVEL_TIME ? level.time :
10100      action_type == CA_SET_LEVEL_SCORE ? 0 :
10101      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10102      action_type == CA_SET_CE_SCORE ? 0 :
10103      0);
10104
10105   int action_arg_number =
10106     (action_arg <= CA_ARG_MAX ? action_arg :
10107      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10108      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10109      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10110      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10111      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10112      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10113      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10114      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10115      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10116      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10117      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10118      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10119      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10120      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10121      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10122      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10123      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10124      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10125      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10126      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10127      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10128      -1);
10129
10130   int action_arg_number_old =
10131     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10132      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10133      action_type == CA_SET_LEVEL_SCORE ? game.score :
10134      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10135      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10136      0);
10137
10138   int action_arg_number_new =
10139     getModifiedActionNumber(action_arg_number_old,
10140                             action_mode, action_arg_number,
10141                             action_arg_number_min, action_arg_number_max);
10142
10143   int trigger_player_bits =
10144     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10145      change->actual_trigger_player_bits : change->trigger_player);
10146
10147   int action_arg_player_bits =
10148     (action_arg >= CA_ARG_PLAYER_1 &&
10149      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10150      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10151      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10152      PLAYER_BITS_ANY);
10153
10154   // ---------- execute action  -----------------------------------------------
10155
10156   switch (action_type)
10157   {
10158     case CA_NO_ACTION:
10159     {
10160       return;
10161     }
10162
10163     // ---------- level actions  ----------------------------------------------
10164
10165     case CA_RESTART_LEVEL:
10166     {
10167       game.restart_level = TRUE;
10168
10169       break;
10170     }
10171
10172     case CA_SHOW_ENVELOPE:
10173     {
10174       int element = getSpecialActionElement(action_arg_element,
10175                                             action_arg_number, EL_ENVELOPE_1);
10176
10177       if (IS_ENVELOPE(element))
10178         local_player->show_envelope = element;
10179
10180       break;
10181     }
10182
10183     case CA_SET_LEVEL_TIME:
10184     {
10185       if (level.time > 0)       // only modify limited time value
10186       {
10187         TimeLeft = action_arg_number_new;
10188
10189         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10190
10191         DisplayGameControlValues();
10192
10193         if (!TimeLeft && setup.time_limit)
10194           for (i = 0; i < MAX_PLAYERS; i++)
10195             KillPlayer(&stored_player[i]);
10196       }
10197
10198       break;
10199     }
10200
10201     case CA_SET_LEVEL_SCORE:
10202     {
10203       game.score = action_arg_number_new;
10204
10205       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10206
10207       DisplayGameControlValues();
10208
10209       break;
10210     }
10211
10212     case CA_SET_LEVEL_GEMS:
10213     {
10214       game.gems_still_needed = action_arg_number_new;
10215
10216       game.snapshot.collected_item = TRUE;
10217
10218       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10219
10220       DisplayGameControlValues();
10221
10222       break;
10223     }
10224
10225     case CA_SET_LEVEL_WIND:
10226     {
10227       game.wind_direction = action_arg_direction;
10228
10229       break;
10230     }
10231
10232     case CA_SET_LEVEL_RANDOM_SEED:
10233     {
10234       // ensure that setting a new random seed while playing is predictable
10235       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10236
10237       break;
10238     }
10239
10240     // ---------- player actions  ---------------------------------------------
10241
10242     case CA_MOVE_PLAYER:
10243     case CA_MOVE_PLAYER_NEW:
10244     {
10245       // automatically move to the next field in specified direction
10246       for (i = 0; i < MAX_PLAYERS; i++)
10247         if (trigger_player_bits & (1 << i))
10248           if (action_type == CA_MOVE_PLAYER ||
10249               stored_player[i].MovPos == 0)
10250             stored_player[i].programmed_action = action_arg_direction;
10251
10252       break;
10253     }
10254
10255     case CA_EXIT_PLAYER:
10256     {
10257       for (i = 0; i < MAX_PLAYERS; i++)
10258         if (action_arg_player_bits & (1 << i))
10259           ExitPlayer(&stored_player[i]);
10260
10261       if (game.players_still_needed == 0)
10262         LevelSolved();
10263
10264       break;
10265     }
10266
10267     case CA_KILL_PLAYER:
10268     {
10269       for (i = 0; i < MAX_PLAYERS; i++)
10270         if (action_arg_player_bits & (1 << i))
10271           KillPlayer(&stored_player[i]);
10272
10273       break;
10274     }
10275
10276     case CA_SET_PLAYER_KEYS:
10277     {
10278       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10279       int element = getSpecialActionElement(action_arg_element,
10280                                             action_arg_number, EL_KEY_1);
10281
10282       if (IS_KEY(element))
10283       {
10284         for (i = 0; i < MAX_PLAYERS; i++)
10285         {
10286           if (trigger_player_bits & (1 << i))
10287           {
10288             stored_player[i].key[KEY_NR(element)] = key_state;
10289
10290             DrawGameDoorValues();
10291           }
10292         }
10293       }
10294
10295       break;
10296     }
10297
10298     case CA_SET_PLAYER_SPEED:
10299     {
10300       for (i = 0; i < MAX_PLAYERS; i++)
10301       {
10302         if (trigger_player_bits & (1 << i))
10303         {
10304           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10305
10306           if (action_arg == CA_ARG_SPEED_FASTER &&
10307               stored_player[i].cannot_move)
10308           {
10309             action_arg_number = STEPSIZE_VERY_SLOW;
10310           }
10311           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10312                    action_arg == CA_ARG_SPEED_FASTER)
10313           {
10314             action_arg_number = 2;
10315             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10316                            CA_MODE_MULTIPLY);
10317           }
10318           else if (action_arg == CA_ARG_NUMBER_RESET)
10319           {
10320             action_arg_number = level.initial_player_stepsize[i];
10321           }
10322
10323           move_stepsize =
10324             getModifiedActionNumber(move_stepsize,
10325                                     action_mode,
10326                                     action_arg_number,
10327                                     action_arg_number_min,
10328                                     action_arg_number_max);
10329
10330           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10331         }
10332       }
10333
10334       break;
10335     }
10336
10337     case CA_SET_PLAYER_SHIELD:
10338     {
10339       for (i = 0; i < MAX_PLAYERS; i++)
10340       {
10341         if (trigger_player_bits & (1 << i))
10342         {
10343           if (action_arg == CA_ARG_SHIELD_OFF)
10344           {
10345             stored_player[i].shield_normal_time_left = 0;
10346             stored_player[i].shield_deadly_time_left = 0;
10347           }
10348           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10349           {
10350             stored_player[i].shield_normal_time_left = 999999;
10351           }
10352           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10353           {
10354             stored_player[i].shield_normal_time_left = 999999;
10355             stored_player[i].shield_deadly_time_left = 999999;
10356           }
10357         }
10358       }
10359
10360       break;
10361     }
10362
10363     case CA_SET_PLAYER_GRAVITY:
10364     {
10365       for (i = 0; i < MAX_PLAYERS; i++)
10366       {
10367         if (trigger_player_bits & (1 << i))
10368         {
10369           stored_player[i].gravity =
10370             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10371              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10372              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10373              stored_player[i].gravity);
10374         }
10375       }
10376
10377       break;
10378     }
10379
10380     case CA_SET_PLAYER_ARTWORK:
10381     {
10382       for (i = 0; i < MAX_PLAYERS; i++)
10383       {
10384         if (trigger_player_bits & (1 << i))
10385         {
10386           int artwork_element = action_arg_element;
10387
10388           if (action_arg == CA_ARG_ELEMENT_RESET)
10389             artwork_element =
10390               (level.use_artwork_element[i] ? level.artwork_element[i] :
10391                stored_player[i].element_nr);
10392
10393           if (stored_player[i].artwork_element != artwork_element)
10394             stored_player[i].Frame = 0;
10395
10396           stored_player[i].artwork_element = artwork_element;
10397
10398           SetPlayerWaiting(&stored_player[i], FALSE);
10399
10400           // set number of special actions for bored and sleeping animation
10401           stored_player[i].num_special_action_bored =
10402             get_num_special_action(artwork_element,
10403                                    ACTION_BORING_1, ACTION_BORING_LAST);
10404           stored_player[i].num_special_action_sleeping =
10405             get_num_special_action(artwork_element,
10406                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10407         }
10408       }
10409
10410       break;
10411     }
10412
10413     case CA_SET_PLAYER_INVENTORY:
10414     {
10415       for (i = 0; i < MAX_PLAYERS; i++)
10416       {
10417         struct PlayerInfo *player = &stored_player[i];
10418         int j, k;
10419
10420         if (trigger_player_bits & (1 << i))
10421         {
10422           int inventory_element = action_arg_element;
10423
10424           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10425               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10426               action_arg == CA_ARG_ELEMENT_ACTION)
10427           {
10428             int element = inventory_element;
10429             int collect_count = element_info[element].collect_count_initial;
10430
10431             if (!IS_CUSTOM_ELEMENT(element))
10432               collect_count = 1;
10433
10434             if (collect_count == 0)
10435               player->inventory_infinite_element = element;
10436             else
10437               for (k = 0; k < collect_count; k++)
10438                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10439                   player->inventory_element[player->inventory_size++] =
10440                     element;
10441           }
10442           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10443                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10444                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10445           {
10446             if (player->inventory_infinite_element != EL_UNDEFINED &&
10447                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10448                                      action_arg_element_raw))
10449               player->inventory_infinite_element = EL_UNDEFINED;
10450
10451             for (k = 0, j = 0; j < player->inventory_size; j++)
10452             {
10453               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10454                                         action_arg_element_raw))
10455                 player->inventory_element[k++] = player->inventory_element[j];
10456             }
10457
10458             player->inventory_size = k;
10459           }
10460           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10461           {
10462             if (player->inventory_size > 0)
10463             {
10464               for (j = 0; j < player->inventory_size - 1; j++)
10465                 player->inventory_element[j] = player->inventory_element[j + 1];
10466
10467               player->inventory_size--;
10468             }
10469           }
10470           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10471           {
10472             if (player->inventory_size > 0)
10473               player->inventory_size--;
10474           }
10475           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10476           {
10477             player->inventory_infinite_element = EL_UNDEFINED;
10478             player->inventory_size = 0;
10479           }
10480           else if (action_arg == CA_ARG_INVENTORY_RESET)
10481           {
10482             player->inventory_infinite_element = EL_UNDEFINED;
10483             player->inventory_size = 0;
10484
10485             if (level.use_initial_inventory[i])
10486             {
10487               for (j = 0; j < level.initial_inventory_size[i]; j++)
10488               {
10489                 int element = level.initial_inventory_content[i][j];
10490                 int collect_count = element_info[element].collect_count_initial;
10491
10492                 if (!IS_CUSTOM_ELEMENT(element))
10493                   collect_count = 1;
10494
10495                 if (collect_count == 0)
10496                   player->inventory_infinite_element = element;
10497                 else
10498                   for (k = 0; k < collect_count; k++)
10499                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10500                       player->inventory_element[player->inventory_size++] =
10501                         element;
10502               }
10503             }
10504           }
10505         }
10506       }
10507
10508       break;
10509     }
10510
10511     // ---------- CE actions  -------------------------------------------------
10512
10513     case CA_SET_CE_VALUE:
10514     {
10515       int last_ce_value = CustomValue[x][y];
10516
10517       CustomValue[x][y] = action_arg_number_new;
10518
10519       if (CustomValue[x][y] != last_ce_value)
10520       {
10521         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10522         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10523
10524         if (CustomValue[x][y] == 0)
10525         {
10526           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10527           ChangeCount[x][y] = 0;        // allow at least one more change
10528
10529           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10530           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10531         }
10532       }
10533
10534       break;
10535     }
10536
10537     case CA_SET_CE_SCORE:
10538     {
10539       int last_ce_score = ei->collect_score;
10540
10541       ei->collect_score = action_arg_number_new;
10542
10543       if (ei->collect_score != last_ce_score)
10544       {
10545         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10546         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10547
10548         if (ei->collect_score == 0)
10549         {
10550           int xx, yy;
10551
10552           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10553           ChangeCount[x][y] = 0;        // allow at least one more change
10554
10555           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10556           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10557
10558           /*
10559             This is a very special case that seems to be a mixture between
10560             CheckElementChange() and CheckTriggeredElementChange(): while
10561             the first one only affects single elements that are triggered
10562             directly, the second one affects multiple elements in the playfield
10563             that are triggered indirectly by another element. This is a third
10564             case: Changing the CE score always affects multiple identical CEs,
10565             so every affected CE must be checked, not only the single CE for
10566             which the CE score was changed in the first place (as every instance
10567             of that CE shares the same CE score, and therefore also can change)!
10568           */
10569           SCAN_PLAYFIELD(xx, yy)
10570           {
10571             if (Tile[xx][yy] == element)
10572               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10573                                  CE_SCORE_GETS_ZERO);
10574           }
10575         }
10576       }
10577
10578       break;
10579     }
10580
10581     case CA_SET_CE_ARTWORK:
10582     {
10583       int artwork_element = action_arg_element;
10584       boolean reset_frame = FALSE;
10585       int xx, yy;
10586
10587       if (action_arg == CA_ARG_ELEMENT_RESET)
10588         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10589                            element);
10590
10591       if (ei->gfx_element != artwork_element)
10592         reset_frame = TRUE;
10593
10594       ei->gfx_element = artwork_element;
10595
10596       SCAN_PLAYFIELD(xx, yy)
10597       {
10598         if (Tile[xx][yy] == element)
10599         {
10600           if (reset_frame)
10601           {
10602             ResetGfxAnimation(xx, yy);
10603             ResetRandomAnimationValue(xx, yy);
10604           }
10605
10606           TEST_DrawLevelField(xx, yy);
10607         }
10608       }
10609
10610       break;
10611     }
10612
10613     // ---------- engine actions  ---------------------------------------------
10614
10615     case CA_SET_ENGINE_SCAN_MODE:
10616     {
10617       InitPlayfieldScanMode(action_arg);
10618
10619       break;
10620     }
10621
10622     default:
10623       break;
10624   }
10625 }
10626
10627 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10628 {
10629   int old_element = Tile[x][y];
10630   int new_element = GetElementFromGroupElement(element);
10631   int previous_move_direction = MovDir[x][y];
10632   int last_ce_value = CustomValue[x][y];
10633   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10634   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10635   boolean add_player_onto_element = (new_element_is_player &&
10636                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10637                                      IS_WALKABLE(old_element));
10638
10639   if (!add_player_onto_element)
10640   {
10641     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10642       RemoveMovingField(x, y);
10643     else
10644       RemoveField(x, y);
10645
10646     Tile[x][y] = new_element;
10647
10648     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10649       MovDir[x][y] = previous_move_direction;
10650
10651     if (element_info[new_element].use_last_ce_value)
10652       CustomValue[x][y] = last_ce_value;
10653
10654     InitField_WithBug1(x, y, FALSE);
10655
10656     new_element = Tile[x][y];   // element may have changed
10657
10658     ResetGfxAnimation(x, y);
10659     ResetRandomAnimationValue(x, y);
10660
10661     TEST_DrawLevelField(x, y);
10662
10663     if (GFX_CRUMBLED(new_element))
10664       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10665   }
10666
10667   // check if element under the player changes from accessible to unaccessible
10668   // (needed for special case of dropping element which then changes)
10669   // (must be checked after creating new element for walkable group elements)
10670   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10671       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10672   {
10673     Bang(x, y);
10674
10675     return;
10676   }
10677
10678   // "ChangeCount" not set yet to allow "entered by player" change one time
10679   if (new_element_is_player)
10680     RelocatePlayer(x, y, new_element);
10681
10682   if (is_change)
10683     ChangeCount[x][y]++;        // count number of changes in the same frame
10684
10685   TestIfBadThingTouchesPlayer(x, y);
10686   TestIfPlayerTouchesCustomElement(x, y);
10687   TestIfElementTouchesCustomElement(x, y);
10688 }
10689
10690 static void CreateField(int x, int y, int element)
10691 {
10692   CreateFieldExt(x, y, element, FALSE);
10693 }
10694
10695 static void CreateElementFromChange(int x, int y, int element)
10696 {
10697   element = GET_VALID_RUNTIME_ELEMENT(element);
10698
10699   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10700   {
10701     int old_element = Tile[x][y];
10702
10703     // prevent changed element from moving in same engine frame
10704     // unless both old and new element can either fall or move
10705     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10706         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10707       Stop[x][y] = TRUE;
10708   }
10709
10710   CreateFieldExt(x, y, element, TRUE);
10711 }
10712
10713 static boolean ChangeElement(int x, int y, int element, int page)
10714 {
10715   struct ElementInfo *ei = &element_info[element];
10716   struct ElementChangeInfo *change = &ei->change_page[page];
10717   int ce_value = CustomValue[x][y];
10718   int ce_score = ei->collect_score;
10719   int target_element;
10720   int old_element = Tile[x][y];
10721
10722   // always use default change event to prevent running into a loop
10723   if (ChangeEvent[x][y] == -1)
10724     ChangeEvent[x][y] = CE_DELAY;
10725
10726   if (ChangeEvent[x][y] == CE_DELAY)
10727   {
10728     // reset actual trigger element, trigger player and action element
10729     change->actual_trigger_element = EL_EMPTY;
10730     change->actual_trigger_player = EL_EMPTY;
10731     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10732     change->actual_trigger_side = CH_SIDE_NONE;
10733     change->actual_trigger_ce_value = 0;
10734     change->actual_trigger_ce_score = 0;
10735   }
10736
10737   // do not change elements more than a specified maximum number of changes
10738   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10739     return FALSE;
10740
10741   ChangeCount[x][y]++;          // count number of changes in the same frame
10742
10743   if (change->explode)
10744   {
10745     Bang(x, y);
10746
10747     return TRUE;
10748   }
10749
10750   if (change->use_target_content)
10751   {
10752     boolean complete_replace = TRUE;
10753     boolean can_replace[3][3];
10754     int xx, yy;
10755
10756     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10757     {
10758       boolean is_empty;
10759       boolean is_walkable;
10760       boolean is_diggable;
10761       boolean is_collectible;
10762       boolean is_removable;
10763       boolean is_destructible;
10764       int ex = x + xx - 1;
10765       int ey = y + yy - 1;
10766       int content_element = change->target_content.e[xx][yy];
10767       int e;
10768
10769       can_replace[xx][yy] = TRUE;
10770
10771       if (ex == x && ey == y)   // do not check changing element itself
10772         continue;
10773
10774       if (content_element == EL_EMPTY_SPACE)
10775       {
10776         can_replace[xx][yy] = FALSE;    // do not replace border with space
10777
10778         continue;
10779       }
10780
10781       if (!IN_LEV_FIELD(ex, ey))
10782       {
10783         can_replace[xx][yy] = FALSE;
10784         complete_replace = FALSE;
10785
10786         continue;
10787       }
10788
10789       e = Tile[ex][ey];
10790
10791       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10792         e = MovingOrBlocked2Element(ex, ey);
10793
10794       is_empty = (IS_FREE(ex, ey) ||
10795                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10796
10797       is_walkable     = (is_empty || IS_WALKABLE(e));
10798       is_diggable     = (is_empty || IS_DIGGABLE(e));
10799       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10800       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10801       is_removable    = (is_diggable || is_collectible);
10802
10803       can_replace[xx][yy] =
10804         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10805           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10806           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10807           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10808           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10809           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10810          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10811
10812       if (!can_replace[xx][yy])
10813         complete_replace = FALSE;
10814     }
10815
10816     if (!change->only_if_complete || complete_replace)
10817     {
10818       boolean something_has_changed = FALSE;
10819
10820       if (change->only_if_complete && change->use_random_replace &&
10821           RND(100) < change->random_percentage)
10822         return FALSE;
10823
10824       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10825       {
10826         int ex = x + xx - 1;
10827         int ey = y + yy - 1;
10828         int content_element;
10829
10830         if (can_replace[xx][yy] && (!change->use_random_replace ||
10831                                     RND(100) < change->random_percentage))
10832         {
10833           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10834             RemoveMovingField(ex, ey);
10835
10836           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10837
10838           content_element = change->target_content.e[xx][yy];
10839           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10840                                               ce_value, ce_score);
10841
10842           CreateElementFromChange(ex, ey, target_element);
10843
10844           something_has_changed = TRUE;
10845
10846           // for symmetry reasons, freeze newly created border elements
10847           if (ex != x || ey != y)
10848             Stop[ex][ey] = TRUE;        // no more moving in this frame
10849         }
10850       }
10851
10852       if (something_has_changed)
10853       {
10854         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10855         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10856       }
10857     }
10858   }
10859   else
10860   {
10861     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10862                                         ce_value, ce_score);
10863
10864     if (element == EL_DIAGONAL_GROWING ||
10865         element == EL_DIAGONAL_SHRINKING)
10866     {
10867       target_element = Store[x][y];
10868
10869       Store[x][y] = EL_EMPTY;
10870     }
10871
10872     // special case: element changes to player (and may be kept if walkable)
10873     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10874       CreateElementFromChange(x, y, EL_EMPTY);
10875
10876     CreateElementFromChange(x, y, target_element);
10877
10878     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10879     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10880   }
10881
10882   // this uses direct change before indirect change
10883   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10884
10885   return TRUE;
10886 }
10887
10888 static void HandleElementChange(int x, int y, int page)
10889 {
10890   int element = MovingOrBlocked2Element(x, y);
10891   struct ElementInfo *ei = &element_info[element];
10892   struct ElementChangeInfo *change = &ei->change_page[page];
10893   boolean handle_action_before_change = FALSE;
10894
10895 #ifdef DEBUG
10896   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10897       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10898   {
10899     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10900           x, y, element, element_info[element].token_name);
10901     Debug("game:playing:HandleElementChange", "This should never happen!");
10902   }
10903 #endif
10904
10905   // this can happen with classic bombs on walkable, changing elements
10906   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10907   {
10908     return;
10909   }
10910
10911   if (ChangeDelay[x][y] == 0)           // initialize element change
10912   {
10913     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10914
10915     if (change->can_change)
10916     {
10917       // !!! not clear why graphic animation should be reset at all here !!!
10918       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10919       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10920
10921       /*
10922         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10923
10924         When using an animation frame delay of 1 (this only happens with
10925         "sp_zonk.moving.left/right" in the classic graphics), the default
10926         (non-moving) animation shows wrong animation frames (while the
10927         moving animation, like "sp_zonk.moving.left/right", is correct,
10928         so this graphical bug never shows up with the classic graphics).
10929         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10930         be drawn instead of the correct frames 0,1,2,3. This is caused by
10931         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10932         an element change: First when the change delay ("ChangeDelay[][]")
10933         counter has reached zero after decrementing, then a second time in
10934         the next frame (after "GfxFrame[][]" was already incremented) when
10935         "ChangeDelay[][]" is reset to the initial delay value again.
10936
10937         This causes frame 0 to be drawn twice, while the last frame won't
10938         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10939
10940         As some animations may already be cleverly designed around this bug
10941         (at least the "Snake Bite" snake tail animation does this), it cannot
10942         simply be fixed here without breaking such existing animations.
10943         Unfortunately, it cannot easily be detected if a graphics set was
10944         designed "before" or "after" the bug was fixed. As a workaround,
10945         a new graphics set option "game.graphics_engine_version" was added
10946         to be able to specify the game's major release version for which the
10947         graphics set was designed, which can then be used to decide if the
10948         bugfix should be used (version 4 and above) or not (version 3 or
10949         below, or if no version was specified at all, as with old sets).
10950
10951         (The wrong/fixed animation frames can be tested with the test level set
10952         "test_gfxframe" and level "000", which contains a specially prepared
10953         custom element at level position (x/y) == (11/9) which uses the zonk
10954         animation mentioned above. Using "game.graphics_engine_version: 4"
10955         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10956         This can also be seen from the debug output for this test element.)
10957       */
10958
10959       // when a custom element is about to change (for example by change delay),
10960       // do not reset graphic animation when the custom element is moving
10961       if (game.graphics_engine_version < 4 &&
10962           !IS_MOVING(x, y))
10963       {
10964         ResetGfxAnimation(x, y);
10965         ResetRandomAnimationValue(x, y);
10966       }
10967
10968       if (change->pre_change_function)
10969         change->pre_change_function(x, y);
10970     }
10971   }
10972
10973   ChangeDelay[x][y]--;
10974
10975   if (ChangeDelay[x][y] != 0)           // continue element change
10976   {
10977     if (change->can_change)
10978     {
10979       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10980
10981       if (IS_ANIMATED(graphic))
10982         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10983
10984       if (change->change_function)
10985         change->change_function(x, y);
10986     }
10987   }
10988   else                                  // finish element change
10989   {
10990     if (ChangePage[x][y] != -1)         // remember page from delayed change
10991     {
10992       page = ChangePage[x][y];
10993       ChangePage[x][y] = -1;
10994
10995       change = &ei->change_page[page];
10996     }
10997
10998     if (IS_MOVING(x, y))                // never change a running system ;-)
10999     {
11000       ChangeDelay[x][y] = 1;            // try change after next move step
11001       ChangePage[x][y] = page;          // remember page to use for change
11002
11003       return;
11004     }
11005
11006     // special case: set new level random seed before changing element
11007     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11008       handle_action_before_change = TRUE;
11009
11010     if (change->has_action && handle_action_before_change)
11011       ExecuteCustomElementAction(x, y, element, page);
11012
11013     if (change->can_change)
11014     {
11015       if (ChangeElement(x, y, element, page))
11016       {
11017         if (change->post_change_function)
11018           change->post_change_function(x, y);
11019       }
11020     }
11021
11022     if (change->has_action && !handle_action_before_change)
11023       ExecuteCustomElementAction(x, y, element, page);
11024   }
11025 }
11026
11027 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11028                                               int trigger_element,
11029                                               int trigger_event,
11030                                               int trigger_player,
11031                                               int trigger_side,
11032                                               int trigger_page)
11033 {
11034   boolean change_done_any = FALSE;
11035   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11036   int i;
11037
11038   if (!(trigger_events[trigger_element][trigger_event]))
11039     return FALSE;
11040
11041   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11042
11043   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11044   {
11045     int element = EL_CUSTOM_START + i;
11046     boolean change_done = FALSE;
11047     int p;
11048
11049     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11050         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11051       continue;
11052
11053     for (p = 0; p < element_info[element].num_change_pages; p++)
11054     {
11055       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11056
11057       if (change->can_change_or_has_action &&
11058           change->has_event[trigger_event] &&
11059           change->trigger_side & trigger_side &&
11060           change->trigger_player & trigger_player &&
11061           change->trigger_page & trigger_page_bits &&
11062           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11063       {
11064         change->actual_trigger_element = trigger_element;
11065         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11066         change->actual_trigger_player_bits = trigger_player;
11067         change->actual_trigger_side = trigger_side;
11068         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11069         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11070
11071         if ((change->can_change && !change_done) || change->has_action)
11072         {
11073           int x, y;
11074
11075           SCAN_PLAYFIELD(x, y)
11076           {
11077             if (Tile[x][y] == element)
11078             {
11079               if (change->can_change && !change_done)
11080               {
11081                 // if element already changed in this frame, not only prevent
11082                 // another element change (checked in ChangeElement()), but
11083                 // also prevent additional element actions for this element
11084
11085                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11086                     !level.use_action_after_change_bug)
11087                   continue;
11088
11089                 ChangeDelay[x][y] = 1;
11090                 ChangeEvent[x][y] = trigger_event;
11091
11092                 HandleElementChange(x, y, p);
11093               }
11094               else if (change->has_action)
11095               {
11096                 // if element already changed in this frame, not only prevent
11097                 // another element change (checked in ChangeElement()), but
11098                 // also prevent additional element actions for this element
11099
11100                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11101                     !level.use_action_after_change_bug)
11102                   continue;
11103
11104                 ExecuteCustomElementAction(x, y, element, p);
11105                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11106               }
11107             }
11108           }
11109
11110           if (change->can_change)
11111           {
11112             change_done = TRUE;
11113             change_done_any = TRUE;
11114           }
11115         }
11116       }
11117     }
11118   }
11119
11120   RECURSION_LOOP_DETECTION_END();
11121
11122   return change_done_any;
11123 }
11124
11125 static boolean CheckElementChangeExt(int x, int y,
11126                                      int element,
11127                                      int trigger_element,
11128                                      int trigger_event,
11129                                      int trigger_player,
11130                                      int trigger_side)
11131 {
11132   boolean change_done = FALSE;
11133   int p;
11134
11135   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11136       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11137     return FALSE;
11138
11139   if (Tile[x][y] == EL_BLOCKED)
11140   {
11141     Blocked2Moving(x, y, &x, &y);
11142     element = Tile[x][y];
11143   }
11144
11145   // check if element has already changed or is about to change after moving
11146   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11147        Tile[x][y] != element) ||
11148
11149       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11150        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11151         ChangePage[x][y] != -1)))
11152     return FALSE;
11153
11154   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11155
11156   for (p = 0; p < element_info[element].num_change_pages; p++)
11157   {
11158     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11159
11160     /* check trigger element for all events where the element that is checked
11161        for changing interacts with a directly adjacent element -- this is
11162        different to element changes that affect other elements to change on the
11163        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11164     boolean check_trigger_element =
11165       (trigger_event == CE_NEXT_TO_X ||
11166        trigger_event == CE_TOUCHING_X ||
11167        trigger_event == CE_HITTING_X ||
11168        trigger_event == CE_HIT_BY_X ||
11169        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11170
11171     if (change->can_change_or_has_action &&
11172         change->has_event[trigger_event] &&
11173         change->trigger_side & trigger_side &&
11174         change->trigger_player & trigger_player &&
11175         (!check_trigger_element ||
11176          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11177     {
11178       change->actual_trigger_element = trigger_element;
11179       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11180       change->actual_trigger_player_bits = trigger_player;
11181       change->actual_trigger_side = trigger_side;
11182       change->actual_trigger_ce_value = CustomValue[x][y];
11183       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11184
11185       // special case: trigger element not at (x,y) position for some events
11186       if (check_trigger_element)
11187       {
11188         static struct
11189         {
11190           int dx, dy;
11191         } move_xy[] =
11192           {
11193             {  0,  0 },
11194             { -1,  0 },
11195             { +1,  0 },
11196             {  0,  0 },
11197             {  0, -1 },
11198             {  0,  0 }, { 0, 0 }, { 0, 0 },
11199             {  0, +1 }
11200           };
11201
11202         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11203         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11204
11205         change->actual_trigger_ce_value = CustomValue[xx][yy];
11206         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11207       }
11208
11209       if (change->can_change && !change_done)
11210       {
11211         ChangeDelay[x][y] = 1;
11212         ChangeEvent[x][y] = trigger_event;
11213
11214         HandleElementChange(x, y, p);
11215
11216         change_done = TRUE;
11217       }
11218       else if (change->has_action)
11219       {
11220         ExecuteCustomElementAction(x, y, element, p);
11221         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11222       }
11223     }
11224   }
11225
11226   RECURSION_LOOP_DETECTION_END();
11227
11228   return change_done;
11229 }
11230
11231 static void PlayPlayerSound(struct PlayerInfo *player)
11232 {
11233   int jx = player->jx, jy = player->jy;
11234   int sound_element = player->artwork_element;
11235   int last_action = player->last_action_waiting;
11236   int action = player->action_waiting;
11237
11238   if (player->is_waiting)
11239   {
11240     if (action != last_action)
11241       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11242     else
11243       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11244   }
11245   else
11246   {
11247     if (action != last_action)
11248       StopSound(element_info[sound_element].sound[last_action]);
11249
11250     if (last_action == ACTION_SLEEPING)
11251       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11252   }
11253 }
11254
11255 static void PlayAllPlayersSound(void)
11256 {
11257   int i;
11258
11259   for (i = 0; i < MAX_PLAYERS; i++)
11260     if (stored_player[i].active)
11261       PlayPlayerSound(&stored_player[i]);
11262 }
11263
11264 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11265 {
11266   boolean last_waiting = player->is_waiting;
11267   int move_dir = player->MovDir;
11268
11269   player->dir_waiting = move_dir;
11270   player->last_action_waiting = player->action_waiting;
11271
11272   if (is_waiting)
11273   {
11274     if (!last_waiting)          // not waiting -> waiting
11275     {
11276       player->is_waiting = TRUE;
11277
11278       player->frame_counter_bored =
11279         FrameCounter +
11280         game.player_boring_delay_fixed +
11281         GetSimpleRandom(game.player_boring_delay_random);
11282       player->frame_counter_sleeping =
11283         FrameCounter +
11284         game.player_sleeping_delay_fixed +
11285         GetSimpleRandom(game.player_sleeping_delay_random);
11286
11287       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11288     }
11289
11290     if (game.player_sleeping_delay_fixed +
11291         game.player_sleeping_delay_random > 0 &&
11292         player->anim_delay_counter == 0 &&
11293         player->post_delay_counter == 0 &&
11294         FrameCounter >= player->frame_counter_sleeping)
11295       player->is_sleeping = TRUE;
11296     else if (game.player_boring_delay_fixed +
11297              game.player_boring_delay_random > 0 &&
11298              FrameCounter >= player->frame_counter_bored)
11299       player->is_bored = TRUE;
11300
11301     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11302                               player->is_bored ? ACTION_BORING :
11303                               ACTION_WAITING);
11304
11305     if (player->is_sleeping && player->use_murphy)
11306     {
11307       // special case for sleeping Murphy when leaning against non-free tile
11308
11309       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11310           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11311            !IS_MOVING(player->jx - 1, player->jy)))
11312         move_dir = MV_LEFT;
11313       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11314                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11315                 !IS_MOVING(player->jx + 1, player->jy)))
11316         move_dir = MV_RIGHT;
11317       else
11318         player->is_sleeping = FALSE;
11319
11320       player->dir_waiting = move_dir;
11321     }
11322
11323     if (player->is_sleeping)
11324     {
11325       if (player->num_special_action_sleeping > 0)
11326       {
11327         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11328         {
11329           int last_special_action = player->special_action_sleeping;
11330           int num_special_action = player->num_special_action_sleeping;
11331           int special_action =
11332             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11333              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11334              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11335              last_special_action + 1 : ACTION_SLEEPING);
11336           int special_graphic =
11337             el_act_dir2img(player->artwork_element, special_action, move_dir);
11338
11339           player->anim_delay_counter =
11340             graphic_info[special_graphic].anim_delay_fixed +
11341             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11342           player->post_delay_counter =
11343             graphic_info[special_graphic].post_delay_fixed +
11344             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11345
11346           player->special_action_sleeping = special_action;
11347         }
11348
11349         if (player->anim_delay_counter > 0)
11350         {
11351           player->action_waiting = player->special_action_sleeping;
11352           player->anim_delay_counter--;
11353         }
11354         else if (player->post_delay_counter > 0)
11355         {
11356           player->post_delay_counter--;
11357         }
11358       }
11359     }
11360     else if (player->is_bored)
11361     {
11362       if (player->num_special_action_bored > 0)
11363       {
11364         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11365         {
11366           int special_action =
11367             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11368           int special_graphic =
11369             el_act_dir2img(player->artwork_element, special_action, move_dir);
11370
11371           player->anim_delay_counter =
11372             graphic_info[special_graphic].anim_delay_fixed +
11373             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11374           player->post_delay_counter =
11375             graphic_info[special_graphic].post_delay_fixed +
11376             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11377
11378           player->special_action_bored = special_action;
11379         }
11380
11381         if (player->anim_delay_counter > 0)
11382         {
11383           player->action_waiting = player->special_action_bored;
11384           player->anim_delay_counter--;
11385         }
11386         else if (player->post_delay_counter > 0)
11387         {
11388           player->post_delay_counter--;
11389         }
11390       }
11391     }
11392   }
11393   else if (last_waiting)        // waiting -> not waiting
11394   {
11395     player->is_waiting = FALSE;
11396     player->is_bored = FALSE;
11397     player->is_sleeping = FALSE;
11398
11399     player->frame_counter_bored = -1;
11400     player->frame_counter_sleeping = -1;
11401
11402     player->anim_delay_counter = 0;
11403     player->post_delay_counter = 0;
11404
11405     player->dir_waiting = player->MovDir;
11406     player->action_waiting = ACTION_DEFAULT;
11407
11408     player->special_action_bored = ACTION_DEFAULT;
11409     player->special_action_sleeping = ACTION_DEFAULT;
11410   }
11411 }
11412
11413 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11414 {
11415   if ((!player->is_moving  && player->was_moving) ||
11416       (player->MovPos == 0 && player->was_moving) ||
11417       (player->is_snapping && !player->was_snapping) ||
11418       (player->is_dropping && !player->was_dropping))
11419   {
11420     if (!CheckSaveEngineSnapshotToList())
11421       return;
11422
11423     player->was_moving = FALSE;
11424     player->was_snapping = TRUE;
11425     player->was_dropping = TRUE;
11426   }
11427   else
11428   {
11429     if (player->is_moving)
11430       player->was_moving = TRUE;
11431
11432     if (!player->is_snapping)
11433       player->was_snapping = FALSE;
11434
11435     if (!player->is_dropping)
11436       player->was_dropping = FALSE;
11437   }
11438
11439   static struct MouseActionInfo mouse_action_last = { 0 };
11440   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11441   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11442
11443   if (new_released)
11444     CheckSaveEngineSnapshotToList();
11445
11446   mouse_action_last = mouse_action;
11447 }
11448
11449 static void CheckSingleStepMode(struct PlayerInfo *player)
11450 {
11451   if (tape.single_step && tape.recording && !tape.pausing)
11452   {
11453     // as it is called "single step mode", just return to pause mode when the
11454     // player stopped moving after one tile (or never starts moving at all)
11455     // (reverse logic needed here in case single step mode used in team mode)
11456     if (player->is_moving ||
11457         player->is_pushing ||
11458         player->is_dropping_pressed ||
11459         player->effective_mouse_action.button)
11460       game.enter_single_step_mode = FALSE;
11461   }
11462
11463   CheckSaveEngineSnapshot(player);
11464 }
11465
11466 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11467 {
11468   int left      = player_action & JOY_LEFT;
11469   int right     = player_action & JOY_RIGHT;
11470   int up        = player_action & JOY_UP;
11471   int down      = player_action & JOY_DOWN;
11472   int button1   = player_action & JOY_BUTTON_1;
11473   int button2   = player_action & JOY_BUTTON_2;
11474   int dx        = (left ? -1 : right ? 1 : 0);
11475   int dy        = (up   ? -1 : down  ? 1 : 0);
11476
11477   if (!player->active || tape.pausing)
11478     return 0;
11479
11480   if (player_action)
11481   {
11482     if (button1)
11483       SnapField(player, dx, dy);
11484     else
11485     {
11486       if (button2)
11487         DropElement(player);
11488
11489       MovePlayer(player, dx, dy);
11490     }
11491
11492     CheckSingleStepMode(player);
11493
11494     SetPlayerWaiting(player, FALSE);
11495
11496     return player_action;
11497   }
11498   else
11499   {
11500     // no actions for this player (no input at player's configured device)
11501
11502     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11503     SnapField(player, 0, 0);
11504     CheckGravityMovementWhenNotMoving(player);
11505
11506     if (player->MovPos == 0)
11507       SetPlayerWaiting(player, TRUE);
11508
11509     if (player->MovPos == 0)    // needed for tape.playing
11510       player->is_moving = FALSE;
11511
11512     player->is_dropping = FALSE;
11513     player->is_dropping_pressed = FALSE;
11514     player->drop_pressed_delay = 0;
11515
11516     CheckSingleStepMode(player);
11517
11518     return 0;
11519   }
11520 }
11521
11522 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11523                                          byte *tape_action)
11524 {
11525   if (!tape.use_mouse_actions)
11526     return;
11527
11528   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11529   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11530   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11531 }
11532
11533 static void SetTapeActionFromMouseAction(byte *tape_action,
11534                                          struct MouseActionInfo *mouse_action)
11535 {
11536   if (!tape.use_mouse_actions)
11537     return;
11538
11539   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11540   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11541   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11542 }
11543
11544 static void CheckLevelSolved(void)
11545 {
11546   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11547   {
11548     if (game_em.level_solved &&
11549         !game_em.game_over)                             // game won
11550     {
11551       LevelSolved();
11552
11553       game_em.game_over = TRUE;
11554
11555       game.all_players_gone = TRUE;
11556     }
11557
11558     if (game_em.game_over)                              // game lost
11559       game.all_players_gone = TRUE;
11560   }
11561   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11562   {
11563     if (game_sp.level_solved &&
11564         !game_sp.game_over)                             // game won
11565     {
11566       LevelSolved();
11567
11568       game_sp.game_over = TRUE;
11569
11570       game.all_players_gone = TRUE;
11571     }
11572
11573     if (game_sp.game_over)                              // game lost
11574       game.all_players_gone = TRUE;
11575   }
11576   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11577   {
11578     if (game_mm.level_solved &&
11579         !game_mm.game_over)                             // game won
11580     {
11581       LevelSolved();
11582
11583       game_mm.game_over = TRUE;
11584
11585       game.all_players_gone = TRUE;
11586     }
11587
11588     if (game_mm.game_over)                              // game lost
11589       game.all_players_gone = TRUE;
11590   }
11591 }
11592
11593 static void CheckLevelTime(void)
11594 {
11595   int i;
11596
11597   if (TimeFrames >= FRAMES_PER_SECOND)
11598   {
11599     TimeFrames = 0;
11600     TapeTime++;
11601
11602     for (i = 0; i < MAX_PLAYERS; i++)
11603     {
11604       struct PlayerInfo *player = &stored_player[i];
11605
11606       if (SHIELD_ON(player))
11607       {
11608         player->shield_normal_time_left--;
11609
11610         if (player->shield_deadly_time_left > 0)
11611           player->shield_deadly_time_left--;
11612       }
11613     }
11614
11615     if (!game.LevelSolved && !level.use_step_counter)
11616     {
11617       TimePlayed++;
11618
11619       if (TimeLeft > 0)
11620       {
11621         TimeLeft--;
11622
11623         if (TimeLeft <= 10 && setup.time_limit)
11624           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11625
11626         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11627            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11628
11629         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11630
11631         if (!TimeLeft && setup.time_limit)
11632         {
11633           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11634             game_em.lev->killed_out_of_time = TRUE;
11635           else
11636             for (i = 0; i < MAX_PLAYERS; i++)
11637               KillPlayer(&stored_player[i]);
11638         }
11639       }
11640       else if (game.no_time_limit && !game.all_players_gone)
11641       {
11642         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11643       }
11644
11645       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11646     }
11647
11648     if (tape.recording || tape.playing)
11649       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11650   }
11651
11652   if (tape.recording || tape.playing)
11653     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11654
11655   UpdateAndDisplayGameControlValues();
11656 }
11657
11658 void AdvanceFrameAndPlayerCounters(int player_nr)
11659 {
11660   int i;
11661
11662   // advance frame counters (global frame counter and time frame counter)
11663   FrameCounter++;
11664   TimeFrames++;
11665
11666   // advance player counters (counters for move delay, move animation etc.)
11667   for (i = 0; i < MAX_PLAYERS; i++)
11668   {
11669     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11670     int move_delay_value = stored_player[i].move_delay_value;
11671     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11672
11673     if (!advance_player_counters)       // not all players may be affected
11674       continue;
11675
11676     if (move_frames == 0)       // less than one move per game frame
11677     {
11678       int stepsize = TILEX / move_delay_value;
11679       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11680       int count = (stored_player[i].is_moving ?
11681                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11682
11683       if (count % delay == 0)
11684         move_frames = 1;
11685     }
11686
11687     stored_player[i].Frame += move_frames;
11688
11689     if (stored_player[i].MovPos != 0)
11690       stored_player[i].StepFrame += move_frames;
11691
11692     if (stored_player[i].move_delay > 0)
11693       stored_player[i].move_delay--;
11694
11695     // due to bugs in previous versions, counter must count up, not down
11696     if (stored_player[i].push_delay != -1)
11697       stored_player[i].push_delay++;
11698
11699     if (stored_player[i].drop_delay > 0)
11700       stored_player[i].drop_delay--;
11701
11702     if (stored_player[i].is_dropping_pressed)
11703       stored_player[i].drop_pressed_delay++;
11704   }
11705 }
11706
11707 void StartGameActions(boolean init_network_game, boolean record_tape,
11708                       int random_seed)
11709 {
11710   unsigned int new_random_seed = InitRND(random_seed);
11711
11712   if (record_tape)
11713     TapeStartRecording(new_random_seed);
11714
11715   if (init_network_game)
11716   {
11717     SendToServer_LevelFile();
11718     SendToServer_StartPlaying();
11719
11720     return;
11721   }
11722
11723   InitGame();
11724 }
11725
11726 static void GameActionsExt(void)
11727 {
11728 #if 0
11729   static unsigned int game_frame_delay = 0;
11730 #endif
11731   unsigned int game_frame_delay_value;
11732   byte *recorded_player_action;
11733   byte summarized_player_action = 0;
11734   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11735   int i;
11736
11737   // detect endless loops, caused by custom element programming
11738   if (recursion_loop_detected && recursion_loop_depth == 0)
11739   {
11740     char *message = getStringCat3("Internal Error! Element ",
11741                                   EL_NAME(recursion_loop_element),
11742                                   " caused endless loop! Quit the game?");
11743
11744     Warn("element '%s' caused endless loop in game engine",
11745          EL_NAME(recursion_loop_element));
11746
11747     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11748
11749     recursion_loop_detected = FALSE;    // if game should be continued
11750
11751     free(message);
11752
11753     return;
11754   }
11755
11756   if (game.restart_level)
11757     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11758
11759   CheckLevelSolved();
11760
11761   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11762     GameWon();
11763
11764   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11765     TapeStop();
11766
11767   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11768     return;
11769
11770   game_frame_delay_value =
11771     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11772
11773   if (tape.playing && tape.warp_forward && !tape.pausing)
11774     game_frame_delay_value = 0;
11775
11776   SetVideoFrameDelay(game_frame_delay_value);
11777
11778   // (de)activate virtual buttons depending on current game status
11779   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11780   {
11781     if (game.all_players_gone)  // if no players there to be controlled anymore
11782       SetOverlayActive(FALSE);
11783     else if (!tape.playing)     // if game continues after tape stopped playing
11784       SetOverlayActive(TRUE);
11785   }
11786
11787 #if 0
11788 #if 0
11789   // ---------- main game synchronization point ----------
11790
11791   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11792
11793   Debug("game:playing:skip", "skip == %d", skip);
11794
11795 #else
11796   // ---------- main game synchronization point ----------
11797
11798   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11799 #endif
11800 #endif
11801
11802   if (network_playing && !network_player_action_received)
11803   {
11804     // try to get network player actions in time
11805
11806     // last chance to get network player actions without main loop delay
11807     HandleNetworking();
11808
11809     // game was quit by network peer
11810     if (game_status != GAME_MODE_PLAYING)
11811       return;
11812
11813     // check if network player actions still missing and game still running
11814     if (!network_player_action_received && !checkGameEnded())
11815       return;           // failed to get network player actions in time
11816
11817     // do not yet reset "network_player_action_received" (for tape.pausing)
11818   }
11819
11820   if (tape.pausing)
11821     return;
11822
11823   // at this point we know that we really continue executing the game
11824
11825   network_player_action_received = FALSE;
11826
11827   // when playing tape, read previously recorded player input from tape data
11828   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11829
11830   local_player->effective_mouse_action = local_player->mouse_action;
11831
11832   if (recorded_player_action != NULL)
11833     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11834                                  recorded_player_action);
11835
11836   // TapePlayAction() may return NULL when toggling to "pause before death"
11837   if (tape.pausing)
11838     return;
11839
11840   if (tape.set_centered_player)
11841   {
11842     game.centered_player_nr_next = tape.centered_player_nr_next;
11843     game.set_centered_player = TRUE;
11844   }
11845
11846   for (i = 0; i < MAX_PLAYERS; i++)
11847   {
11848     summarized_player_action |= stored_player[i].action;
11849
11850     if (!network_playing && (game.team_mode || tape.playing))
11851       stored_player[i].effective_action = stored_player[i].action;
11852   }
11853
11854   if (network_playing && !checkGameEnded())
11855     SendToServer_MovePlayer(summarized_player_action);
11856
11857   // summarize all actions at local players mapped input device position
11858   // (this allows using different input devices in single player mode)
11859   if (!network.enabled && !game.team_mode)
11860     stored_player[map_player_action[local_player->index_nr]].effective_action =
11861       summarized_player_action;
11862
11863   // summarize all actions at centered player in local team mode
11864   if (tape.recording &&
11865       setup.team_mode && !network.enabled &&
11866       setup.input_on_focus &&
11867       game.centered_player_nr != -1)
11868   {
11869     for (i = 0; i < MAX_PLAYERS; i++)
11870       stored_player[map_player_action[i]].effective_action =
11871         (i == game.centered_player_nr ? summarized_player_action : 0);
11872   }
11873
11874   if (recorded_player_action != NULL)
11875     for (i = 0; i < MAX_PLAYERS; i++)
11876       stored_player[i].effective_action = recorded_player_action[i];
11877
11878   for (i = 0; i < MAX_PLAYERS; i++)
11879   {
11880     tape_action[i] = stored_player[i].effective_action;
11881
11882     /* (this may happen in the RND game engine if a player was not present on
11883        the playfield on level start, but appeared later from a custom element */
11884     if (setup.team_mode &&
11885         tape.recording &&
11886         tape_action[i] &&
11887         !tape.player_participates[i])
11888       tape.player_participates[i] = TRUE;
11889   }
11890
11891   SetTapeActionFromMouseAction(tape_action,
11892                                &local_player->effective_mouse_action);
11893
11894   // only record actions from input devices, but not programmed actions
11895   if (tape.recording)
11896     TapeRecordAction(tape_action);
11897
11898   // remember if game was played (especially after tape stopped playing)
11899   if (!tape.playing && summarized_player_action)
11900     game.GamePlayed = TRUE;
11901
11902 #if USE_NEW_PLAYER_ASSIGNMENTS
11903   // !!! also map player actions in single player mode !!!
11904   // if (game.team_mode)
11905   if (1)
11906   {
11907     byte mapped_action[MAX_PLAYERS];
11908
11909 #if DEBUG_PLAYER_ACTIONS
11910     for (i = 0; i < MAX_PLAYERS; i++)
11911       DebugContinued("", "%d, ", stored_player[i].effective_action);
11912 #endif
11913
11914     for (i = 0; i < MAX_PLAYERS; i++)
11915       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11916
11917     for (i = 0; i < MAX_PLAYERS; i++)
11918       stored_player[i].effective_action = mapped_action[i];
11919
11920 #if DEBUG_PLAYER_ACTIONS
11921     DebugContinued("", "=> ");
11922     for (i = 0; i < MAX_PLAYERS; i++)
11923       DebugContinued("", "%d, ", stored_player[i].effective_action);
11924     DebugContinued("game:playing:player", "\n");
11925 #endif
11926   }
11927 #if DEBUG_PLAYER_ACTIONS
11928   else
11929   {
11930     for (i = 0; i < MAX_PLAYERS; i++)
11931       DebugContinued("", "%d, ", stored_player[i].effective_action);
11932     DebugContinued("game:playing:player", "\n");
11933   }
11934 #endif
11935 #endif
11936
11937   for (i = 0; i < MAX_PLAYERS; i++)
11938   {
11939     // allow engine snapshot in case of changed movement attempt
11940     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11941         (stored_player[i].effective_action & KEY_MOTION))
11942       game.snapshot.changed_action = TRUE;
11943
11944     // allow engine snapshot in case of snapping/dropping attempt
11945     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11946         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11947       game.snapshot.changed_action = TRUE;
11948
11949     game.snapshot.last_action[i] = stored_player[i].effective_action;
11950   }
11951
11952   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11953   {
11954     GameActions_EM_Main();
11955   }
11956   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11957   {
11958     GameActions_SP_Main();
11959   }
11960   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11961   {
11962     GameActions_MM_Main();
11963   }
11964   else
11965   {
11966     GameActions_RND_Main();
11967   }
11968
11969   BlitScreenToBitmap(backbuffer);
11970
11971   CheckLevelSolved();
11972   CheckLevelTime();
11973
11974   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11975
11976   if (global.show_frames_per_second)
11977   {
11978     static unsigned int fps_counter = 0;
11979     static int fps_frames = 0;
11980     unsigned int fps_delay_ms = Counter() - fps_counter;
11981
11982     fps_frames++;
11983
11984     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11985     {
11986       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11987
11988       fps_frames = 0;
11989       fps_counter = Counter();
11990
11991       // always draw FPS to screen after FPS value was updated
11992       redraw_mask |= REDRAW_FPS;
11993     }
11994
11995     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11996     if (GetDrawDeactivationMask() == REDRAW_NONE)
11997       redraw_mask |= REDRAW_FPS;
11998   }
11999 }
12000
12001 static void GameActions_CheckSaveEngineSnapshot(void)
12002 {
12003   if (!game.snapshot.save_snapshot)
12004     return;
12005
12006   // clear flag for saving snapshot _before_ saving snapshot
12007   game.snapshot.save_snapshot = FALSE;
12008
12009   SaveEngineSnapshotToList();
12010 }
12011
12012 void GameActions(void)
12013 {
12014   GameActionsExt();
12015
12016   GameActions_CheckSaveEngineSnapshot();
12017 }
12018
12019 void GameActions_EM_Main(void)
12020 {
12021   byte effective_action[MAX_PLAYERS];
12022   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12023   int i;
12024
12025   for (i = 0; i < MAX_PLAYERS; i++)
12026     effective_action[i] = stored_player[i].effective_action;
12027
12028   GameActions_EM(effective_action, warp_mode);
12029 }
12030
12031 void GameActions_SP_Main(void)
12032 {
12033   byte effective_action[MAX_PLAYERS];
12034   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12035   int i;
12036
12037   for (i = 0; i < MAX_PLAYERS; i++)
12038     effective_action[i] = stored_player[i].effective_action;
12039
12040   GameActions_SP(effective_action, warp_mode);
12041
12042   for (i = 0; i < MAX_PLAYERS; i++)
12043   {
12044     if (stored_player[i].force_dropping)
12045       stored_player[i].action |= KEY_BUTTON_DROP;
12046
12047     stored_player[i].force_dropping = FALSE;
12048   }
12049 }
12050
12051 void GameActions_MM_Main(void)
12052 {
12053   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12054
12055   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12056 }
12057
12058 void GameActions_RND_Main(void)
12059 {
12060   GameActions_RND();
12061 }
12062
12063 void GameActions_RND(void)
12064 {
12065   static struct MouseActionInfo mouse_action_last = { 0 };
12066   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12067   int magic_wall_x = 0, magic_wall_y = 0;
12068   int i, x, y, element, graphic, last_gfx_frame;
12069
12070   InitPlayfieldScanModeVars();
12071
12072   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12073   {
12074     SCAN_PLAYFIELD(x, y)
12075     {
12076       ChangeCount[x][y] = 0;
12077       ChangeEvent[x][y] = -1;
12078     }
12079   }
12080
12081   if (game.set_centered_player)
12082   {
12083     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12084
12085     // switching to "all players" only possible if all players fit to screen
12086     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12087     {
12088       game.centered_player_nr_next = game.centered_player_nr;
12089       game.set_centered_player = FALSE;
12090     }
12091
12092     // do not switch focus to non-existing (or non-active) player
12093     if (game.centered_player_nr_next >= 0 &&
12094         !stored_player[game.centered_player_nr_next].active)
12095     {
12096       game.centered_player_nr_next = game.centered_player_nr;
12097       game.set_centered_player = FALSE;
12098     }
12099   }
12100
12101   if (game.set_centered_player &&
12102       ScreenMovPos == 0)        // screen currently aligned at tile position
12103   {
12104     int sx, sy;
12105
12106     if (game.centered_player_nr_next == -1)
12107     {
12108       setScreenCenteredToAllPlayers(&sx, &sy);
12109     }
12110     else
12111     {
12112       sx = stored_player[game.centered_player_nr_next].jx;
12113       sy = stored_player[game.centered_player_nr_next].jy;
12114     }
12115
12116     game.centered_player_nr = game.centered_player_nr_next;
12117     game.set_centered_player = FALSE;
12118
12119     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12120     DrawGameDoorValues();
12121   }
12122
12123   // check single step mode (set flag and clear again if any player is active)
12124   game.enter_single_step_mode =
12125     (tape.single_step && tape.recording && !tape.pausing);
12126
12127   for (i = 0; i < MAX_PLAYERS; i++)
12128   {
12129     int actual_player_action = stored_player[i].effective_action;
12130
12131 #if 1
12132     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12133        - rnd_equinox_tetrachloride 048
12134        - rnd_equinox_tetrachloride_ii 096
12135        - rnd_emanuel_schmieg 002
12136        - doctor_sloan_ww 001, 020
12137     */
12138     if (stored_player[i].MovPos == 0)
12139       CheckGravityMovement(&stored_player[i]);
12140 #endif
12141
12142     // overwrite programmed action with tape action
12143     if (stored_player[i].programmed_action)
12144       actual_player_action = stored_player[i].programmed_action;
12145
12146     PlayerActions(&stored_player[i], actual_player_action);
12147
12148     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12149   }
12150
12151   // single step pause mode may already have been toggled by "ScrollPlayer()"
12152   if (game.enter_single_step_mode && !tape.pausing)
12153     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12154
12155   ScrollScreen(NULL, SCROLL_GO_ON);
12156
12157   /* for backwards compatibility, the following code emulates a fixed bug that
12158      occured when pushing elements (causing elements that just made their last
12159      pushing step to already (if possible) make their first falling step in the
12160      same game frame, which is bad); this code is also needed to use the famous
12161      "spring push bug" which is used in older levels and might be wanted to be
12162      used also in newer levels, but in this case the buggy pushing code is only
12163      affecting the "spring" element and no other elements */
12164
12165   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12166   {
12167     for (i = 0; i < MAX_PLAYERS; i++)
12168     {
12169       struct PlayerInfo *player = &stored_player[i];
12170       int x = player->jx;
12171       int y = player->jy;
12172
12173       if (player->active && player->is_pushing && player->is_moving &&
12174           IS_MOVING(x, y) &&
12175           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12176            Tile[x][y] == EL_SPRING))
12177       {
12178         ContinueMoving(x, y);
12179
12180         // continue moving after pushing (this is actually a bug)
12181         if (!IS_MOVING(x, y))
12182           Stop[x][y] = FALSE;
12183       }
12184     }
12185   }
12186
12187   SCAN_PLAYFIELD(x, y)
12188   {
12189     Last[x][y] = Tile[x][y];
12190
12191     ChangeCount[x][y] = 0;
12192     ChangeEvent[x][y] = -1;
12193
12194     // this must be handled before main playfield loop
12195     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12196     {
12197       MovDelay[x][y]--;
12198       if (MovDelay[x][y] <= 0)
12199         RemoveField(x, y);
12200     }
12201
12202     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12203     {
12204       MovDelay[x][y]--;
12205       if (MovDelay[x][y] <= 0)
12206       {
12207         int element = Store[x][y];
12208         int move_direction = MovDir[x][y];
12209         int player_index_bit = Store2[x][y];
12210
12211         Store[x][y] = 0;
12212         Store2[x][y] = 0;
12213
12214         RemoveField(x, y);
12215         TEST_DrawLevelField(x, y);
12216
12217         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12218
12219         if (IS_ENVELOPE(element))
12220           local_player->show_envelope = element;
12221       }
12222     }
12223
12224 #if DEBUG
12225     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12226     {
12227       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12228             x, y);
12229       Debug("game:playing:GameActions_RND", "This should never happen!");
12230
12231       ChangePage[x][y] = -1;
12232     }
12233 #endif
12234
12235     Stop[x][y] = FALSE;
12236     if (WasJustMoving[x][y] > 0)
12237       WasJustMoving[x][y]--;
12238     if (WasJustFalling[x][y] > 0)
12239       WasJustFalling[x][y]--;
12240     if (CheckCollision[x][y] > 0)
12241       CheckCollision[x][y]--;
12242     if (CheckImpact[x][y] > 0)
12243       CheckImpact[x][y]--;
12244
12245     GfxFrame[x][y]++;
12246
12247     /* reset finished pushing action (not done in ContinueMoving() to allow
12248        continuous pushing animation for elements with zero push delay) */
12249     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12250     {
12251       ResetGfxAnimation(x, y);
12252       TEST_DrawLevelField(x, y);
12253     }
12254
12255 #if DEBUG
12256     if (IS_BLOCKED(x, y))
12257     {
12258       int oldx, oldy;
12259
12260       Blocked2Moving(x, y, &oldx, &oldy);
12261       if (!IS_MOVING(oldx, oldy))
12262       {
12263         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12264         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12265         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12266         Debug("game:playing:GameActions_RND", "This should never happen!");
12267       }
12268     }
12269 #endif
12270   }
12271
12272   if (mouse_action.button)
12273   {
12274     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12275     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12276
12277     x = mouse_action.lx;
12278     y = mouse_action.ly;
12279     element = Tile[x][y];
12280
12281     if (new_button)
12282     {
12283       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12284       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12285                                          ch_button);
12286     }
12287
12288     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12289     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12290                                        ch_button);
12291   }
12292
12293   SCAN_PLAYFIELD(x, y)
12294   {
12295     element = Tile[x][y];
12296     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12297     last_gfx_frame = GfxFrame[x][y];
12298
12299     ResetGfxFrame(x, y);
12300
12301     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12302       DrawLevelGraphicAnimation(x, y, graphic);
12303
12304     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12305         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12306       ResetRandomAnimationValue(x, y);
12307
12308     SetRandomAnimationValue(x, y);
12309
12310     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12311
12312     if (IS_INACTIVE(element))
12313     {
12314       if (IS_ANIMATED(graphic))
12315         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12316
12317       continue;
12318     }
12319
12320     // this may take place after moving, so 'element' may have changed
12321     if (IS_CHANGING(x, y) &&
12322         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12323     {
12324       int page = element_info[element].event_page_nr[CE_DELAY];
12325
12326       HandleElementChange(x, y, page);
12327
12328       element = Tile[x][y];
12329       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12330     }
12331
12332     CheckNextToConditions(x, y);
12333
12334     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12335     {
12336       StartMoving(x, y);
12337
12338       element = Tile[x][y];
12339       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12340
12341       if (IS_ANIMATED(graphic) &&
12342           !IS_MOVING(x, y) &&
12343           !Stop[x][y])
12344         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12345
12346       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12347         TEST_DrawTwinkleOnField(x, y);
12348     }
12349     else if (element == EL_ACID)
12350     {
12351       if (!Stop[x][y])
12352         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12353     }
12354     else if ((element == EL_EXIT_OPEN ||
12355               element == EL_EM_EXIT_OPEN ||
12356               element == EL_SP_EXIT_OPEN ||
12357               element == EL_STEEL_EXIT_OPEN ||
12358               element == EL_EM_STEEL_EXIT_OPEN ||
12359               element == EL_SP_TERMINAL ||
12360               element == EL_SP_TERMINAL_ACTIVE ||
12361               element == EL_EXTRA_TIME ||
12362               element == EL_SHIELD_NORMAL ||
12363               element == EL_SHIELD_DEADLY) &&
12364              IS_ANIMATED(graphic))
12365       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12366     else if (IS_MOVING(x, y))
12367       ContinueMoving(x, y);
12368     else if (IS_ACTIVE_BOMB(element))
12369       CheckDynamite(x, y);
12370     else if (element == EL_AMOEBA_GROWING)
12371       AmoebaGrowing(x, y);
12372     else if (element == EL_AMOEBA_SHRINKING)
12373       AmoebaShrinking(x, y);
12374
12375 #if !USE_NEW_AMOEBA_CODE
12376     else if (IS_AMOEBALIVE(element))
12377       AmoebaReproduce(x, y);
12378 #endif
12379
12380     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12381       Life(x, y);
12382     else if (element == EL_EXIT_CLOSED)
12383       CheckExit(x, y);
12384     else if (element == EL_EM_EXIT_CLOSED)
12385       CheckExitEM(x, y);
12386     else if (element == EL_STEEL_EXIT_CLOSED)
12387       CheckExitSteel(x, y);
12388     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12389       CheckExitSteelEM(x, y);
12390     else if (element == EL_SP_EXIT_CLOSED)
12391       CheckExitSP(x, y);
12392     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12393              element == EL_EXPANDABLE_STEELWALL_GROWING)
12394       MauerWaechst(x, y);
12395     else if (element == EL_EXPANDABLE_WALL ||
12396              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12397              element == EL_EXPANDABLE_WALL_VERTICAL ||
12398              element == EL_EXPANDABLE_WALL_ANY ||
12399              element == EL_BD_EXPANDABLE_WALL)
12400       MauerAbleger(x, y);
12401     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12402              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12403              element == EL_EXPANDABLE_STEELWALL_ANY)
12404       MauerAblegerStahl(x, y);
12405     else if (element == EL_FLAMES)
12406       CheckForDragon(x, y);
12407     else if (element == EL_EXPLOSION)
12408       ; // drawing of correct explosion animation is handled separately
12409     else if (element == EL_ELEMENT_SNAPPING ||
12410              element == EL_DIAGONAL_SHRINKING ||
12411              element == EL_DIAGONAL_GROWING)
12412     {
12413       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12414
12415       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12416     }
12417     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12418       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12419
12420     if (IS_BELT_ACTIVE(element))
12421       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12422
12423     if (game.magic_wall_active)
12424     {
12425       int jx = local_player->jx, jy = local_player->jy;
12426
12427       // play the element sound at the position nearest to the player
12428       if ((element == EL_MAGIC_WALL_FULL ||
12429            element == EL_MAGIC_WALL_ACTIVE ||
12430            element == EL_MAGIC_WALL_EMPTYING ||
12431            element == EL_BD_MAGIC_WALL_FULL ||
12432            element == EL_BD_MAGIC_WALL_ACTIVE ||
12433            element == EL_BD_MAGIC_WALL_EMPTYING ||
12434            element == EL_DC_MAGIC_WALL_FULL ||
12435            element == EL_DC_MAGIC_WALL_ACTIVE ||
12436            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12437           ABS(x - jx) + ABS(y - jy) <
12438           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12439       {
12440         magic_wall_x = x;
12441         magic_wall_y = y;
12442       }
12443     }
12444   }
12445
12446 #if USE_NEW_AMOEBA_CODE
12447   // new experimental amoeba growth stuff
12448   if (!(FrameCounter % 8))
12449   {
12450     static unsigned int random = 1684108901;
12451
12452     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12453     {
12454       x = RND(lev_fieldx);
12455       y = RND(lev_fieldy);
12456       element = Tile[x][y];
12457
12458       if (!IS_PLAYER(x,y) &&
12459           (element == EL_EMPTY ||
12460            CAN_GROW_INTO(element) ||
12461            element == EL_QUICKSAND_EMPTY ||
12462            element == EL_QUICKSAND_FAST_EMPTY ||
12463            element == EL_ACID_SPLASH_LEFT ||
12464            element == EL_ACID_SPLASH_RIGHT))
12465       {
12466         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12467             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12468             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12469             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12470           Tile[x][y] = EL_AMOEBA_DROP;
12471       }
12472
12473       random = random * 129 + 1;
12474     }
12475   }
12476 #endif
12477
12478   game.explosions_delayed = FALSE;
12479
12480   SCAN_PLAYFIELD(x, y)
12481   {
12482     element = Tile[x][y];
12483
12484     if (ExplodeField[x][y])
12485       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12486     else if (element == EL_EXPLOSION)
12487       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12488
12489     ExplodeField[x][y] = EX_TYPE_NONE;
12490   }
12491
12492   game.explosions_delayed = TRUE;
12493
12494   if (game.magic_wall_active)
12495   {
12496     if (!(game.magic_wall_time_left % 4))
12497     {
12498       int element = Tile[magic_wall_x][magic_wall_y];
12499
12500       if (element == EL_BD_MAGIC_WALL_FULL ||
12501           element == EL_BD_MAGIC_WALL_ACTIVE ||
12502           element == EL_BD_MAGIC_WALL_EMPTYING)
12503         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12504       else if (element == EL_DC_MAGIC_WALL_FULL ||
12505                element == EL_DC_MAGIC_WALL_ACTIVE ||
12506                element == EL_DC_MAGIC_WALL_EMPTYING)
12507         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12508       else
12509         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12510     }
12511
12512     if (game.magic_wall_time_left > 0)
12513     {
12514       game.magic_wall_time_left--;
12515
12516       if (!game.magic_wall_time_left)
12517       {
12518         SCAN_PLAYFIELD(x, y)
12519         {
12520           element = Tile[x][y];
12521
12522           if (element == EL_MAGIC_WALL_ACTIVE ||
12523               element == EL_MAGIC_WALL_FULL)
12524           {
12525             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12526             TEST_DrawLevelField(x, y);
12527           }
12528           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12529                    element == EL_BD_MAGIC_WALL_FULL)
12530           {
12531             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12532             TEST_DrawLevelField(x, y);
12533           }
12534           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12535                    element == EL_DC_MAGIC_WALL_FULL)
12536           {
12537             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12538             TEST_DrawLevelField(x, y);
12539           }
12540         }
12541
12542         game.magic_wall_active = FALSE;
12543       }
12544     }
12545   }
12546
12547   if (game.light_time_left > 0)
12548   {
12549     game.light_time_left--;
12550
12551     if (game.light_time_left == 0)
12552       RedrawAllLightSwitchesAndInvisibleElements();
12553   }
12554
12555   if (game.timegate_time_left > 0)
12556   {
12557     game.timegate_time_left--;
12558
12559     if (game.timegate_time_left == 0)
12560       CloseAllOpenTimegates();
12561   }
12562
12563   if (game.lenses_time_left > 0)
12564   {
12565     game.lenses_time_left--;
12566
12567     if (game.lenses_time_left == 0)
12568       RedrawAllInvisibleElementsForLenses();
12569   }
12570
12571   if (game.magnify_time_left > 0)
12572   {
12573     game.magnify_time_left--;
12574
12575     if (game.magnify_time_left == 0)
12576       RedrawAllInvisibleElementsForMagnifier();
12577   }
12578
12579   for (i = 0; i < MAX_PLAYERS; i++)
12580   {
12581     struct PlayerInfo *player = &stored_player[i];
12582
12583     if (SHIELD_ON(player))
12584     {
12585       if (player->shield_deadly_time_left)
12586         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12587       else if (player->shield_normal_time_left)
12588         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12589     }
12590   }
12591
12592 #if USE_DELAYED_GFX_REDRAW
12593   SCAN_PLAYFIELD(x, y)
12594   {
12595     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12596     {
12597       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12598          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12599
12600       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12601         DrawLevelField(x, y);
12602
12603       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12604         DrawLevelFieldCrumbled(x, y);
12605
12606       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12607         DrawLevelFieldCrumbledNeighbours(x, y);
12608
12609       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12610         DrawTwinkleOnField(x, y);
12611     }
12612
12613     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12614   }
12615 #endif
12616
12617   DrawAllPlayers();
12618   PlayAllPlayersSound();
12619
12620   for (i = 0; i < MAX_PLAYERS; i++)
12621   {
12622     struct PlayerInfo *player = &stored_player[i];
12623
12624     if (player->show_envelope != 0 && (!player->active ||
12625                                        player->MovPos == 0))
12626     {
12627       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12628
12629       player->show_envelope = 0;
12630     }
12631   }
12632
12633   // use random number generator in every frame to make it less predictable
12634   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12635     RND(1);
12636
12637   mouse_action_last = mouse_action;
12638 }
12639
12640 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12641 {
12642   int min_x = x, min_y = y, max_x = x, max_y = y;
12643   int scr_fieldx = getScreenFieldSizeX();
12644   int scr_fieldy = getScreenFieldSizeY();
12645   int i;
12646
12647   for (i = 0; i < MAX_PLAYERS; i++)
12648   {
12649     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12650
12651     if (!stored_player[i].active || &stored_player[i] == player)
12652       continue;
12653
12654     min_x = MIN(min_x, jx);
12655     min_y = MIN(min_y, jy);
12656     max_x = MAX(max_x, jx);
12657     max_y = MAX(max_y, jy);
12658   }
12659
12660   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12661 }
12662
12663 static boolean AllPlayersInVisibleScreen(void)
12664 {
12665   int i;
12666
12667   for (i = 0; i < MAX_PLAYERS; i++)
12668   {
12669     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12670
12671     if (!stored_player[i].active)
12672       continue;
12673
12674     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12675       return FALSE;
12676   }
12677
12678   return TRUE;
12679 }
12680
12681 void ScrollLevel(int dx, int dy)
12682 {
12683   int scroll_offset = 2 * TILEX_VAR;
12684   int x, y;
12685
12686   BlitBitmap(drawto_field, drawto_field,
12687              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12688              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12689              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12690              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12691              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12692              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12693
12694   if (dx != 0)
12695   {
12696     x = (dx == 1 ? BX1 : BX2);
12697     for (y = BY1; y <= BY2; y++)
12698       DrawScreenField(x, y);
12699   }
12700
12701   if (dy != 0)
12702   {
12703     y = (dy == 1 ? BY1 : BY2);
12704     for (x = BX1; x <= BX2; x++)
12705       DrawScreenField(x, y);
12706   }
12707
12708   redraw_mask |= REDRAW_FIELD;
12709 }
12710
12711 static boolean canFallDown(struct PlayerInfo *player)
12712 {
12713   int jx = player->jx, jy = player->jy;
12714
12715   return (IN_LEV_FIELD(jx, jy + 1) &&
12716           (IS_FREE(jx, jy + 1) ||
12717            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12718           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12719           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12720 }
12721
12722 static boolean canPassField(int x, int y, int move_dir)
12723 {
12724   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12725   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12726   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12727   int nextx = x + dx;
12728   int nexty = y + dy;
12729   int element = Tile[x][y];
12730
12731   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12732           !CAN_MOVE(element) &&
12733           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12734           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12735           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12736 }
12737
12738 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12739 {
12740   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12741   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12742   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12743   int newx = x + dx;
12744   int newy = y + dy;
12745
12746   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12747           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12748           (IS_DIGGABLE(Tile[newx][newy]) ||
12749            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12750            canPassField(newx, newy, move_dir)));
12751 }
12752
12753 static void CheckGravityMovement(struct PlayerInfo *player)
12754 {
12755   if (player->gravity && !player->programmed_action)
12756   {
12757     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12758     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12759     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12760     int jx = player->jx, jy = player->jy;
12761     boolean player_is_moving_to_valid_field =
12762       (!player_is_snapping &&
12763        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12764         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12765     boolean player_can_fall_down = canFallDown(player);
12766
12767     if (player_can_fall_down &&
12768         !player_is_moving_to_valid_field)
12769       player->programmed_action = MV_DOWN;
12770   }
12771 }
12772
12773 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12774 {
12775   return CheckGravityMovement(player);
12776
12777   if (player->gravity && !player->programmed_action)
12778   {
12779     int jx = player->jx, jy = player->jy;
12780     boolean field_under_player_is_free =
12781       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12782     boolean player_is_standing_on_valid_field =
12783       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12784        (IS_WALKABLE(Tile[jx][jy]) &&
12785         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12786
12787     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12788       player->programmed_action = MV_DOWN;
12789   }
12790 }
12791
12792 /*
12793   MovePlayerOneStep()
12794   -----------------------------------------------------------------------------
12795   dx, dy:               direction (non-diagonal) to try to move the player to
12796   real_dx, real_dy:     direction as read from input device (can be diagonal)
12797 */
12798
12799 boolean MovePlayerOneStep(struct PlayerInfo *player,
12800                           int dx, int dy, int real_dx, int real_dy)
12801 {
12802   int jx = player->jx, jy = player->jy;
12803   int new_jx = jx + dx, new_jy = jy + dy;
12804   int can_move;
12805   boolean player_can_move = !player->cannot_move;
12806
12807   if (!player->active || (!dx && !dy))
12808     return MP_NO_ACTION;
12809
12810   player->MovDir = (dx < 0 ? MV_LEFT :
12811                     dx > 0 ? MV_RIGHT :
12812                     dy < 0 ? MV_UP :
12813                     dy > 0 ? MV_DOWN :  MV_NONE);
12814
12815   if (!IN_LEV_FIELD(new_jx, new_jy))
12816     return MP_NO_ACTION;
12817
12818   if (!player_can_move)
12819   {
12820     if (player->MovPos == 0)
12821     {
12822       player->is_moving = FALSE;
12823       player->is_digging = FALSE;
12824       player->is_collecting = FALSE;
12825       player->is_snapping = FALSE;
12826       player->is_pushing = FALSE;
12827     }
12828   }
12829
12830   if (!network.enabled && game.centered_player_nr == -1 &&
12831       !AllPlayersInSight(player, new_jx, new_jy))
12832     return MP_NO_ACTION;
12833
12834   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12835   if (can_move != MP_MOVING)
12836     return can_move;
12837
12838   // check if DigField() has caused relocation of the player
12839   if (player->jx != jx || player->jy != jy)
12840     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12841
12842   StorePlayer[jx][jy] = 0;
12843   player->last_jx = jx;
12844   player->last_jy = jy;
12845   player->jx = new_jx;
12846   player->jy = new_jy;
12847   StorePlayer[new_jx][new_jy] = player->element_nr;
12848
12849   if (player->move_delay_value_next != -1)
12850   {
12851     player->move_delay_value = player->move_delay_value_next;
12852     player->move_delay_value_next = -1;
12853   }
12854
12855   player->MovPos =
12856     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12857
12858   player->step_counter++;
12859
12860   PlayerVisit[jx][jy] = FrameCounter;
12861
12862   player->is_moving = TRUE;
12863
12864 #if 1
12865   // should better be called in MovePlayer(), but this breaks some tapes
12866   ScrollPlayer(player, SCROLL_INIT);
12867 #endif
12868
12869   return MP_MOVING;
12870 }
12871
12872 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12873 {
12874   int jx = player->jx, jy = player->jy;
12875   int old_jx = jx, old_jy = jy;
12876   int moved = MP_NO_ACTION;
12877
12878   if (!player->active)
12879     return FALSE;
12880
12881   if (!dx && !dy)
12882   {
12883     if (player->MovPos == 0)
12884     {
12885       player->is_moving = FALSE;
12886       player->is_digging = FALSE;
12887       player->is_collecting = FALSE;
12888       player->is_snapping = FALSE;
12889       player->is_pushing = FALSE;
12890     }
12891
12892     return FALSE;
12893   }
12894
12895   if (player->move_delay > 0)
12896     return FALSE;
12897
12898   player->move_delay = -1;              // set to "uninitialized" value
12899
12900   // store if player is automatically moved to next field
12901   player->is_auto_moving = (player->programmed_action != MV_NONE);
12902
12903   // remove the last programmed player action
12904   player->programmed_action = 0;
12905
12906   if (player->MovPos)
12907   {
12908     // should only happen if pre-1.2 tape recordings are played
12909     // this is only for backward compatibility
12910
12911     int original_move_delay_value = player->move_delay_value;
12912
12913 #if DEBUG
12914     Debug("game:playing:MovePlayer",
12915           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12916           tape.counter);
12917 #endif
12918
12919     // scroll remaining steps with finest movement resolution
12920     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12921
12922     while (player->MovPos)
12923     {
12924       ScrollPlayer(player, SCROLL_GO_ON);
12925       ScrollScreen(NULL, SCROLL_GO_ON);
12926
12927       AdvanceFrameAndPlayerCounters(player->index_nr);
12928
12929       DrawAllPlayers();
12930       BackToFront_WithFrameDelay(0);
12931     }
12932
12933     player->move_delay_value = original_move_delay_value;
12934   }
12935
12936   player->is_active = FALSE;
12937
12938   if (player->last_move_dir & MV_HORIZONTAL)
12939   {
12940     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12941       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12942   }
12943   else
12944   {
12945     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12946       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12947   }
12948
12949   if (!moved && !player->is_active)
12950   {
12951     player->is_moving = FALSE;
12952     player->is_digging = FALSE;
12953     player->is_collecting = FALSE;
12954     player->is_snapping = FALSE;
12955     player->is_pushing = FALSE;
12956   }
12957
12958   jx = player->jx;
12959   jy = player->jy;
12960
12961   if (moved & MP_MOVING && !ScreenMovPos &&
12962       (player->index_nr == game.centered_player_nr ||
12963        game.centered_player_nr == -1))
12964   {
12965     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12966
12967     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12968     {
12969       // actual player has left the screen -- scroll in that direction
12970       if (jx != old_jx)         // player has moved horizontally
12971         scroll_x += (jx - old_jx);
12972       else                      // player has moved vertically
12973         scroll_y += (jy - old_jy);
12974     }
12975     else
12976     {
12977       int offset_raw = game.scroll_delay_value;
12978
12979       if (jx != old_jx)         // player has moved horizontally
12980       {
12981         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12982         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12983         int new_scroll_x = jx - MIDPOSX + offset_x;
12984
12985         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12986             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12987           scroll_x = new_scroll_x;
12988
12989         // don't scroll over playfield boundaries
12990         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12991
12992         // don't scroll more than one field at a time
12993         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12994
12995         // don't scroll against the player's moving direction
12996         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12997             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12998           scroll_x = old_scroll_x;
12999       }
13000       else                      // player has moved vertically
13001       {
13002         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13003         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13004         int new_scroll_y = jy - MIDPOSY + offset_y;
13005
13006         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13007             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13008           scroll_y = new_scroll_y;
13009
13010         // don't scroll over playfield boundaries
13011         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13012
13013         // don't scroll more than one field at a time
13014         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13015
13016         // don't scroll against the player's moving direction
13017         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13018             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13019           scroll_y = old_scroll_y;
13020       }
13021     }
13022
13023     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13024     {
13025       if (!network.enabled && game.centered_player_nr == -1 &&
13026           !AllPlayersInVisibleScreen())
13027       {
13028         scroll_x = old_scroll_x;
13029         scroll_y = old_scroll_y;
13030       }
13031       else
13032       {
13033         ScrollScreen(player, SCROLL_INIT);
13034         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13035       }
13036     }
13037   }
13038
13039   player->StepFrame = 0;
13040
13041   if (moved & MP_MOVING)
13042   {
13043     if (old_jx != jx && old_jy == jy)
13044       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13045     else if (old_jx == jx && old_jy != jy)
13046       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13047
13048     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13049
13050     player->last_move_dir = player->MovDir;
13051     player->is_moving = TRUE;
13052     player->is_snapping = FALSE;
13053     player->is_switching = FALSE;
13054     player->is_dropping = FALSE;
13055     player->is_dropping_pressed = FALSE;
13056     player->drop_pressed_delay = 0;
13057
13058 #if 0
13059     // should better be called here than above, but this breaks some tapes
13060     ScrollPlayer(player, SCROLL_INIT);
13061 #endif
13062   }
13063   else
13064   {
13065     CheckGravityMovementWhenNotMoving(player);
13066
13067     player->is_moving = FALSE;
13068
13069     /* at this point, the player is allowed to move, but cannot move right now
13070        (e.g. because of something blocking the way) -- ensure that the player
13071        is also allowed to move in the next frame (in old versions before 3.1.1,
13072        the player was forced to wait again for eight frames before next try) */
13073
13074     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13075       player->move_delay = 0;   // allow direct movement in the next frame
13076   }
13077
13078   if (player->move_delay == -1)         // not yet initialized by DigField()
13079     player->move_delay = player->move_delay_value;
13080
13081   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13082   {
13083     TestIfPlayerTouchesBadThing(jx, jy);
13084     TestIfPlayerTouchesCustomElement(jx, jy);
13085   }
13086
13087   if (!player->active)
13088     RemovePlayer(player);
13089
13090   return moved;
13091 }
13092
13093 void ScrollPlayer(struct PlayerInfo *player, int mode)
13094 {
13095   int jx = player->jx, jy = player->jy;
13096   int last_jx = player->last_jx, last_jy = player->last_jy;
13097   int move_stepsize = TILEX / player->move_delay_value;
13098
13099   if (!player->active)
13100     return;
13101
13102   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13103     return;
13104
13105   if (mode == SCROLL_INIT)
13106   {
13107     player->actual_frame_counter = FrameCounter;
13108     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13109
13110     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13111         Tile[last_jx][last_jy] == EL_EMPTY)
13112     {
13113       int last_field_block_delay = 0;   // start with no blocking at all
13114       int block_delay_adjustment = player->block_delay_adjustment;
13115
13116       // if player blocks last field, add delay for exactly one move
13117       if (player->block_last_field)
13118       {
13119         last_field_block_delay += player->move_delay_value;
13120
13121         // when blocking enabled, prevent moving up despite gravity
13122         if (player->gravity && player->MovDir == MV_UP)
13123           block_delay_adjustment = -1;
13124       }
13125
13126       // add block delay adjustment (also possible when not blocking)
13127       last_field_block_delay += block_delay_adjustment;
13128
13129       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13130       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13131     }
13132
13133     if (player->MovPos != 0)    // player has not yet reached destination
13134       return;
13135   }
13136   else if (!FrameReached(&player->actual_frame_counter, 1))
13137     return;
13138
13139   if (player->MovPos != 0)
13140   {
13141     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13142     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13143
13144     // before DrawPlayer() to draw correct player graphic for this case
13145     if (player->MovPos == 0)
13146       CheckGravityMovement(player);
13147   }
13148
13149   if (player->MovPos == 0)      // player reached destination field
13150   {
13151     if (player->move_delay_reset_counter > 0)
13152     {
13153       player->move_delay_reset_counter--;
13154
13155       if (player->move_delay_reset_counter == 0)
13156       {
13157         // continue with normal speed after quickly moving through gate
13158         HALVE_PLAYER_SPEED(player);
13159
13160         // be able to make the next move without delay
13161         player->move_delay = 0;
13162       }
13163     }
13164
13165     player->last_jx = jx;
13166     player->last_jy = jy;
13167
13168     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13169         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13170         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13171         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13172         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13173         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13174         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13175         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13176     {
13177       ExitPlayer(player);
13178
13179       if (game.players_still_needed == 0 &&
13180           (game.friends_still_needed == 0 ||
13181            IS_SP_ELEMENT(Tile[jx][jy])))
13182         LevelSolved();
13183     }
13184
13185     // this breaks one level: "machine", level 000
13186     {
13187       int move_direction = player->MovDir;
13188       int enter_side = MV_DIR_OPPOSITE(move_direction);
13189       int leave_side = move_direction;
13190       int old_jx = last_jx;
13191       int old_jy = last_jy;
13192       int old_element = Tile[old_jx][old_jy];
13193       int new_element = Tile[jx][jy];
13194
13195       if (IS_CUSTOM_ELEMENT(old_element))
13196         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13197                                    CE_LEFT_BY_PLAYER,
13198                                    player->index_bit, leave_side);
13199
13200       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13201                                           CE_PLAYER_LEAVES_X,
13202                                           player->index_bit, leave_side);
13203
13204       if (IS_CUSTOM_ELEMENT(new_element))
13205         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13206                                    player->index_bit, enter_side);
13207
13208       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13209                                           CE_PLAYER_ENTERS_X,
13210                                           player->index_bit, enter_side);
13211
13212       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13213                                         CE_MOVE_OF_X, move_direction);
13214     }
13215
13216     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13217     {
13218       TestIfPlayerTouchesBadThing(jx, jy);
13219       TestIfPlayerTouchesCustomElement(jx, jy);
13220
13221       /* needed because pushed element has not yet reached its destination,
13222          so it would trigger a change event at its previous field location */
13223       if (!player->is_pushing)
13224         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13225
13226       if (level.finish_dig_collect &&
13227           (player->is_digging || player->is_collecting))
13228       {
13229         int last_element = player->last_removed_element;
13230         int move_direction = player->MovDir;
13231         int enter_side = MV_DIR_OPPOSITE(move_direction);
13232         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13233                             CE_PLAYER_COLLECTS_X);
13234
13235         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13236                                             player->index_bit, enter_side);
13237
13238         player->last_removed_element = EL_UNDEFINED;
13239       }
13240
13241       if (!player->active)
13242         RemovePlayer(player);
13243     }
13244
13245     if (level.use_step_counter)
13246     {
13247       int i;
13248
13249       TimePlayed++;
13250
13251       if (TimeLeft > 0)
13252       {
13253         TimeLeft--;
13254
13255         if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
13256           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13257
13258         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13259
13260         DisplayGameControlValues();
13261
13262         if (!TimeLeft && setup.time_limit && !game.LevelSolved)
13263           for (i = 0; i < MAX_PLAYERS; i++)
13264             KillPlayer(&stored_player[i]);
13265       }
13266       else if (game.no_time_limit && !game.all_players_gone)
13267       {
13268         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13269
13270         DisplayGameControlValues();
13271       }
13272     }
13273
13274     if (tape.single_step && tape.recording && !tape.pausing &&
13275         !player->programmed_action)
13276       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13277
13278     if (!player->programmed_action)
13279       CheckSaveEngineSnapshot(player);
13280   }
13281 }
13282
13283 void ScrollScreen(struct PlayerInfo *player, int mode)
13284 {
13285   static unsigned int screen_frame_counter = 0;
13286
13287   if (mode == SCROLL_INIT)
13288   {
13289     // set scrolling step size according to actual player's moving speed
13290     ScrollStepSize = TILEX / player->move_delay_value;
13291
13292     screen_frame_counter = FrameCounter;
13293     ScreenMovDir = player->MovDir;
13294     ScreenMovPos = player->MovPos;
13295     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13296     return;
13297   }
13298   else if (!FrameReached(&screen_frame_counter, 1))
13299     return;
13300
13301   if (ScreenMovPos)
13302   {
13303     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13304     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13305     redraw_mask |= REDRAW_FIELD;
13306   }
13307   else
13308     ScreenMovDir = MV_NONE;
13309 }
13310
13311 void CheckNextToConditions(int x, int y)
13312 {
13313   int element = Tile[x][y];
13314
13315   if (IS_PLAYER(x, y))
13316     TestIfPlayerNextToCustomElement(x, y);
13317
13318   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13319       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13320     TestIfElementNextToCustomElement(x, y);
13321 }
13322
13323 void TestIfPlayerNextToCustomElement(int x, int y)
13324 {
13325   static int xy[4][2] =
13326   {
13327     { 0, -1 },
13328     { -1, 0 },
13329     { +1, 0 },
13330     { 0, +1 }
13331   };
13332   static int trigger_sides[4][2] =
13333   {
13334     // center side       border side
13335     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13336     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13337     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13338     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13339   };
13340   int i;
13341
13342   if (!IS_PLAYER(x, y))
13343     return;
13344
13345   struct PlayerInfo *player = PLAYERINFO(x, y);
13346
13347   if (player->is_moving)
13348     return;
13349
13350   for (i = 0; i < NUM_DIRECTIONS; i++)
13351   {
13352     int xx = x + xy[i][0];
13353     int yy = y + xy[i][1];
13354     int border_side = trigger_sides[i][1];
13355     int border_element;
13356
13357     if (!IN_LEV_FIELD(xx, yy))
13358       continue;
13359
13360     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13361       continue;         // center and border element not connected
13362
13363     border_element = Tile[xx][yy];
13364
13365     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13366                                player->index_bit, border_side);
13367     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13368                                         CE_PLAYER_NEXT_TO_X,
13369                                         player->index_bit, border_side);
13370
13371     /* use player element that is initially defined in the level playfield,
13372        not the player element that corresponds to the runtime player number
13373        (example: a level that contains EL_PLAYER_3 as the only player would
13374        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13375
13376     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13377                              CE_NEXT_TO_X, border_side);
13378   }
13379 }
13380
13381 void TestIfPlayerTouchesCustomElement(int x, int y)
13382 {
13383   static int xy[4][2] =
13384   {
13385     { 0, -1 },
13386     { -1, 0 },
13387     { +1, 0 },
13388     { 0, +1 }
13389   };
13390   static int trigger_sides[4][2] =
13391   {
13392     // center side       border side
13393     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13394     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13395     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13396     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13397   };
13398   static int touch_dir[4] =
13399   {
13400     MV_LEFT | MV_RIGHT,
13401     MV_UP   | MV_DOWN,
13402     MV_UP   | MV_DOWN,
13403     MV_LEFT | MV_RIGHT
13404   };
13405   int center_element = Tile[x][y];      // should always be non-moving!
13406   int i;
13407
13408   for (i = 0; i < NUM_DIRECTIONS; i++)
13409   {
13410     int xx = x + xy[i][0];
13411     int yy = y + xy[i][1];
13412     int center_side = trigger_sides[i][0];
13413     int border_side = trigger_sides[i][1];
13414     int border_element;
13415
13416     if (!IN_LEV_FIELD(xx, yy))
13417       continue;
13418
13419     if (IS_PLAYER(x, y))                // player found at center element
13420     {
13421       struct PlayerInfo *player = PLAYERINFO(x, y);
13422
13423       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13424         border_element = Tile[xx][yy];          // may be moving!
13425       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13426         border_element = Tile[xx][yy];
13427       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13428         border_element = MovingOrBlocked2Element(xx, yy);
13429       else
13430         continue;               // center and border element do not touch
13431
13432       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13433                                  player->index_bit, border_side);
13434       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13435                                           CE_PLAYER_TOUCHES_X,
13436                                           player->index_bit, border_side);
13437
13438       {
13439         /* use player element that is initially defined in the level playfield,
13440            not the player element that corresponds to the runtime player number
13441            (example: a level that contains EL_PLAYER_3 as the only player would
13442            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13443         int player_element = PLAYERINFO(x, y)->initial_element;
13444
13445         CheckElementChangeBySide(xx, yy, border_element, player_element,
13446                                  CE_TOUCHING_X, border_side);
13447       }
13448     }
13449     else if (IS_PLAYER(xx, yy))         // player found at border element
13450     {
13451       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13452
13453       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13454       {
13455         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13456           continue;             // center and border element do not touch
13457       }
13458
13459       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13460                                  player->index_bit, center_side);
13461       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13462                                           CE_PLAYER_TOUCHES_X,
13463                                           player->index_bit, center_side);
13464
13465       {
13466         /* use player element that is initially defined in the level playfield,
13467            not the player element that corresponds to the runtime player number
13468            (example: a level that contains EL_PLAYER_3 as the only player would
13469            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13470         int player_element = PLAYERINFO(xx, yy)->initial_element;
13471
13472         CheckElementChangeBySide(x, y, center_element, player_element,
13473                                  CE_TOUCHING_X, center_side);
13474       }
13475
13476       break;
13477     }
13478   }
13479 }
13480
13481 void TestIfElementNextToCustomElement(int x, int y)
13482 {
13483   static int xy[4][2] =
13484   {
13485     { 0, -1 },
13486     { -1, 0 },
13487     { +1, 0 },
13488     { 0, +1 }
13489   };
13490   static int trigger_sides[4][2] =
13491   {
13492     // center side      border side
13493     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13494     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13495     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13496     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13497   };
13498   int center_element = Tile[x][y];      // should always be non-moving!
13499   int i;
13500
13501   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13502     return;
13503
13504   for (i = 0; i < NUM_DIRECTIONS; i++)
13505   {
13506     int xx = x + xy[i][0];
13507     int yy = y + xy[i][1];
13508     int border_side = trigger_sides[i][1];
13509     int border_element;
13510
13511     if (!IN_LEV_FIELD(xx, yy))
13512       continue;
13513
13514     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13515       continue;                 // center and border element not connected
13516
13517     border_element = Tile[xx][yy];
13518
13519     // check for change of center element (but change it only once)
13520     if (CheckElementChangeBySide(x, y, center_element, border_element,
13521                                  CE_NEXT_TO_X, border_side))
13522       break;
13523   }
13524 }
13525
13526 void TestIfElementTouchesCustomElement(int x, int y)
13527 {
13528   static int xy[4][2] =
13529   {
13530     { 0, -1 },
13531     { -1, 0 },
13532     { +1, 0 },
13533     { 0, +1 }
13534   };
13535   static int trigger_sides[4][2] =
13536   {
13537     // center side      border side
13538     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13539     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13540     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13541     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13542   };
13543   static int touch_dir[4] =
13544   {
13545     MV_LEFT | MV_RIGHT,
13546     MV_UP   | MV_DOWN,
13547     MV_UP   | MV_DOWN,
13548     MV_LEFT | MV_RIGHT
13549   };
13550   boolean change_center_element = FALSE;
13551   int center_element = Tile[x][y];      // should always be non-moving!
13552   int border_element_old[NUM_DIRECTIONS];
13553   int i;
13554
13555   for (i = 0; i < NUM_DIRECTIONS; i++)
13556   {
13557     int xx = x + xy[i][0];
13558     int yy = y + xy[i][1];
13559     int border_element;
13560
13561     border_element_old[i] = -1;
13562
13563     if (!IN_LEV_FIELD(xx, yy))
13564       continue;
13565
13566     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13567       border_element = Tile[xx][yy];    // may be moving!
13568     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13569       border_element = Tile[xx][yy];
13570     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13571       border_element = MovingOrBlocked2Element(xx, yy);
13572     else
13573       continue;                 // center and border element do not touch
13574
13575     border_element_old[i] = border_element;
13576   }
13577
13578   for (i = 0; i < NUM_DIRECTIONS; i++)
13579   {
13580     int xx = x + xy[i][0];
13581     int yy = y + xy[i][1];
13582     int center_side = trigger_sides[i][0];
13583     int border_element = border_element_old[i];
13584
13585     if (border_element == -1)
13586       continue;
13587
13588     // check for change of border element
13589     CheckElementChangeBySide(xx, yy, border_element, center_element,
13590                              CE_TOUCHING_X, center_side);
13591
13592     // (center element cannot be player, so we dont have to check this here)
13593   }
13594
13595   for (i = 0; i < NUM_DIRECTIONS; i++)
13596   {
13597     int xx = x + xy[i][0];
13598     int yy = y + xy[i][1];
13599     int border_side = trigger_sides[i][1];
13600     int border_element = border_element_old[i];
13601
13602     if (border_element == -1)
13603       continue;
13604
13605     // check for change of center element (but change it only once)
13606     if (!change_center_element)
13607       change_center_element =
13608         CheckElementChangeBySide(x, y, center_element, border_element,
13609                                  CE_TOUCHING_X, border_side);
13610
13611     if (IS_PLAYER(xx, yy))
13612     {
13613       /* use player element that is initially defined in the level playfield,
13614          not the player element that corresponds to the runtime player number
13615          (example: a level that contains EL_PLAYER_3 as the only player would
13616          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13617       int player_element = PLAYERINFO(xx, yy)->initial_element;
13618
13619       CheckElementChangeBySide(x, y, center_element, player_element,
13620                                CE_TOUCHING_X, border_side);
13621     }
13622   }
13623 }
13624
13625 void TestIfElementHitsCustomElement(int x, int y, int direction)
13626 {
13627   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13628   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13629   int hitx = x + dx, hity = y + dy;
13630   int hitting_element = Tile[x][y];
13631   int touched_element;
13632
13633   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13634     return;
13635
13636   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13637                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13638
13639   if (IN_LEV_FIELD(hitx, hity))
13640   {
13641     int opposite_direction = MV_DIR_OPPOSITE(direction);
13642     int hitting_side = direction;
13643     int touched_side = opposite_direction;
13644     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13645                           MovDir[hitx][hity] != direction ||
13646                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13647
13648     object_hit = TRUE;
13649
13650     if (object_hit)
13651     {
13652       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13653                                CE_HITTING_X, touched_side);
13654
13655       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13656                                CE_HIT_BY_X, hitting_side);
13657
13658       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13659                                CE_HIT_BY_SOMETHING, opposite_direction);
13660
13661       if (IS_PLAYER(hitx, hity))
13662       {
13663         /* use player element that is initially defined in the level playfield,
13664            not the player element that corresponds to the runtime player number
13665            (example: a level that contains EL_PLAYER_3 as the only player would
13666            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13667         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13668
13669         CheckElementChangeBySide(x, y, hitting_element, player_element,
13670                                  CE_HITTING_X, touched_side);
13671       }
13672     }
13673   }
13674
13675   // "hitting something" is also true when hitting the playfield border
13676   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13677                            CE_HITTING_SOMETHING, direction);
13678 }
13679
13680 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13681 {
13682   int i, kill_x = -1, kill_y = -1;
13683
13684   int bad_element = -1;
13685   static int test_xy[4][2] =
13686   {
13687     { 0, -1 },
13688     { -1, 0 },
13689     { +1, 0 },
13690     { 0, +1 }
13691   };
13692   static int test_dir[4] =
13693   {
13694     MV_UP,
13695     MV_LEFT,
13696     MV_RIGHT,
13697     MV_DOWN
13698   };
13699
13700   for (i = 0; i < NUM_DIRECTIONS; i++)
13701   {
13702     int test_x, test_y, test_move_dir, test_element;
13703
13704     test_x = good_x + test_xy[i][0];
13705     test_y = good_y + test_xy[i][1];
13706
13707     if (!IN_LEV_FIELD(test_x, test_y))
13708       continue;
13709
13710     test_move_dir =
13711       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13712
13713     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13714
13715     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13716        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13717     */
13718     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13719         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13720     {
13721       kill_x = test_x;
13722       kill_y = test_y;
13723       bad_element = test_element;
13724
13725       break;
13726     }
13727   }
13728
13729   if (kill_x != -1 || kill_y != -1)
13730   {
13731     if (IS_PLAYER(good_x, good_y))
13732     {
13733       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13734
13735       if (player->shield_deadly_time_left > 0 &&
13736           !IS_INDESTRUCTIBLE(bad_element))
13737         Bang(kill_x, kill_y);
13738       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13739         KillPlayer(player);
13740     }
13741     else
13742       Bang(good_x, good_y);
13743   }
13744 }
13745
13746 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13747 {
13748   int i, kill_x = -1, kill_y = -1;
13749   int bad_element = Tile[bad_x][bad_y];
13750   static int test_xy[4][2] =
13751   {
13752     { 0, -1 },
13753     { -1, 0 },
13754     { +1, 0 },
13755     { 0, +1 }
13756   };
13757   static int touch_dir[4] =
13758   {
13759     MV_LEFT | MV_RIGHT,
13760     MV_UP   | MV_DOWN,
13761     MV_UP   | MV_DOWN,
13762     MV_LEFT | MV_RIGHT
13763   };
13764   static int test_dir[4] =
13765   {
13766     MV_UP,
13767     MV_LEFT,
13768     MV_RIGHT,
13769     MV_DOWN
13770   };
13771
13772   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13773     return;
13774
13775   for (i = 0; i < NUM_DIRECTIONS; i++)
13776   {
13777     int test_x, test_y, test_move_dir, test_element;
13778
13779     test_x = bad_x + test_xy[i][0];
13780     test_y = bad_y + test_xy[i][1];
13781
13782     if (!IN_LEV_FIELD(test_x, test_y))
13783       continue;
13784
13785     test_move_dir =
13786       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13787
13788     test_element = Tile[test_x][test_y];
13789
13790     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13791        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13792     */
13793     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13794         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13795     {
13796       // good thing is player or penguin that does not move away
13797       if (IS_PLAYER(test_x, test_y))
13798       {
13799         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13800
13801         if (bad_element == EL_ROBOT && player->is_moving)
13802           continue;     // robot does not kill player if he is moving
13803
13804         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13805         {
13806           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13807             continue;           // center and border element do not touch
13808         }
13809
13810         kill_x = test_x;
13811         kill_y = test_y;
13812
13813         break;
13814       }
13815       else if (test_element == EL_PENGUIN)
13816       {
13817         kill_x = test_x;
13818         kill_y = test_y;
13819
13820         break;
13821       }
13822     }
13823   }
13824
13825   if (kill_x != -1 || kill_y != -1)
13826   {
13827     if (IS_PLAYER(kill_x, kill_y))
13828     {
13829       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13830
13831       if (player->shield_deadly_time_left > 0 &&
13832           !IS_INDESTRUCTIBLE(bad_element))
13833         Bang(bad_x, bad_y);
13834       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13835         KillPlayer(player);
13836     }
13837     else
13838       Bang(kill_x, kill_y);
13839   }
13840 }
13841
13842 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13843 {
13844   int bad_element = Tile[bad_x][bad_y];
13845   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13846   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13847   int test_x = bad_x + dx, test_y = bad_y + dy;
13848   int test_move_dir, test_element;
13849   int kill_x = -1, kill_y = -1;
13850
13851   if (!IN_LEV_FIELD(test_x, test_y))
13852     return;
13853
13854   test_move_dir =
13855     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13856
13857   test_element = Tile[test_x][test_y];
13858
13859   if (test_move_dir != bad_move_dir)
13860   {
13861     // good thing can be player or penguin that does not move away
13862     if (IS_PLAYER(test_x, test_y))
13863     {
13864       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13865
13866       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13867          player as being hit when he is moving towards the bad thing, because
13868          the "get hit by" condition would be lost after the player stops) */
13869       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13870         return;         // player moves away from bad thing
13871
13872       kill_x = test_x;
13873       kill_y = test_y;
13874     }
13875     else if (test_element == EL_PENGUIN)
13876     {
13877       kill_x = test_x;
13878       kill_y = test_y;
13879     }
13880   }
13881
13882   if (kill_x != -1 || kill_y != -1)
13883   {
13884     if (IS_PLAYER(kill_x, kill_y))
13885     {
13886       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13887
13888       if (player->shield_deadly_time_left > 0 &&
13889           !IS_INDESTRUCTIBLE(bad_element))
13890         Bang(bad_x, bad_y);
13891       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13892         KillPlayer(player);
13893     }
13894     else
13895       Bang(kill_x, kill_y);
13896   }
13897 }
13898
13899 void TestIfPlayerTouchesBadThing(int x, int y)
13900 {
13901   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13902 }
13903
13904 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13905 {
13906   TestIfGoodThingHitsBadThing(x, y, move_dir);
13907 }
13908
13909 void TestIfBadThingTouchesPlayer(int x, int y)
13910 {
13911   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13912 }
13913
13914 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13915 {
13916   TestIfBadThingHitsGoodThing(x, y, move_dir);
13917 }
13918
13919 void TestIfFriendTouchesBadThing(int x, int y)
13920 {
13921   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13922 }
13923
13924 void TestIfBadThingTouchesFriend(int x, int y)
13925 {
13926   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13927 }
13928
13929 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13930 {
13931   int i, kill_x = bad_x, kill_y = bad_y;
13932   static int xy[4][2] =
13933   {
13934     { 0, -1 },
13935     { -1, 0 },
13936     { +1, 0 },
13937     { 0, +1 }
13938   };
13939
13940   for (i = 0; i < NUM_DIRECTIONS; i++)
13941   {
13942     int x, y, element;
13943
13944     x = bad_x + xy[i][0];
13945     y = bad_y + xy[i][1];
13946     if (!IN_LEV_FIELD(x, y))
13947       continue;
13948
13949     element = Tile[x][y];
13950     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13951         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13952     {
13953       kill_x = x;
13954       kill_y = y;
13955       break;
13956     }
13957   }
13958
13959   if (kill_x != bad_x || kill_y != bad_y)
13960     Bang(bad_x, bad_y);
13961 }
13962
13963 void KillPlayer(struct PlayerInfo *player)
13964 {
13965   int jx = player->jx, jy = player->jy;
13966
13967   if (!player->active)
13968     return;
13969
13970 #if 0
13971   Debug("game:playing:KillPlayer",
13972         "0: killed == %d, active == %d, reanimated == %d",
13973         player->killed, player->active, player->reanimated);
13974 #endif
13975
13976   /* the following code was introduced to prevent an infinite loop when calling
13977      -> Bang()
13978      -> CheckTriggeredElementChangeExt()
13979      -> ExecuteCustomElementAction()
13980      -> KillPlayer()
13981      -> (infinitely repeating the above sequence of function calls)
13982      which occurs when killing the player while having a CE with the setting
13983      "kill player X when explosion of <player X>"; the solution using a new
13984      field "player->killed" was chosen for backwards compatibility, although
13985      clever use of the fields "player->active" etc. would probably also work */
13986 #if 1
13987   if (player->killed)
13988     return;
13989 #endif
13990
13991   player->killed = TRUE;
13992
13993   // remove accessible field at the player's position
13994   Tile[jx][jy] = EL_EMPTY;
13995
13996   // deactivate shield (else Bang()/Explode() would not work right)
13997   player->shield_normal_time_left = 0;
13998   player->shield_deadly_time_left = 0;
13999
14000 #if 0
14001   Debug("game:playing:KillPlayer",
14002         "1: killed == %d, active == %d, reanimated == %d",
14003         player->killed, player->active, player->reanimated);
14004 #endif
14005
14006   Bang(jx, jy);
14007
14008 #if 0
14009   Debug("game:playing:KillPlayer",
14010         "2: killed == %d, active == %d, reanimated == %d",
14011         player->killed, player->active, player->reanimated);
14012 #endif
14013
14014   if (player->reanimated)       // killed player may have been reanimated
14015     player->killed = player->reanimated = FALSE;
14016   else
14017     BuryPlayer(player);
14018 }
14019
14020 static void KillPlayerUnlessEnemyProtected(int x, int y)
14021 {
14022   if (!PLAYER_ENEMY_PROTECTED(x, y))
14023     KillPlayer(PLAYERINFO(x, y));
14024 }
14025
14026 static void KillPlayerUnlessExplosionProtected(int x, int y)
14027 {
14028   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14029     KillPlayer(PLAYERINFO(x, y));
14030 }
14031
14032 void BuryPlayer(struct PlayerInfo *player)
14033 {
14034   int jx = player->jx, jy = player->jy;
14035
14036   if (!player->active)
14037     return;
14038
14039   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14040   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14041
14042   RemovePlayer(player);
14043
14044   player->buried = TRUE;
14045
14046   if (game.all_players_gone)
14047     game.GameOver = TRUE;
14048 }
14049
14050 void RemovePlayer(struct PlayerInfo *player)
14051 {
14052   int jx = player->jx, jy = player->jy;
14053   int i, found = FALSE;
14054
14055   player->present = FALSE;
14056   player->active = FALSE;
14057
14058   // required for some CE actions (even if the player is not active anymore)
14059   player->MovPos = 0;
14060
14061   if (!ExplodeField[jx][jy])
14062     StorePlayer[jx][jy] = 0;
14063
14064   if (player->is_moving)
14065     TEST_DrawLevelField(player->last_jx, player->last_jy);
14066
14067   for (i = 0; i < MAX_PLAYERS; i++)
14068     if (stored_player[i].active)
14069       found = TRUE;
14070
14071   if (!found)
14072   {
14073     game.all_players_gone = TRUE;
14074     game.GameOver = TRUE;
14075   }
14076
14077   game.exit_x = game.robot_wheel_x = jx;
14078   game.exit_y = game.robot_wheel_y = jy;
14079 }
14080
14081 void ExitPlayer(struct PlayerInfo *player)
14082 {
14083   DrawPlayer(player);   // needed here only to cleanup last field
14084   RemovePlayer(player);
14085
14086   if (game.players_still_needed > 0)
14087     game.players_still_needed--;
14088 }
14089
14090 static void SetFieldForSnapping(int x, int y, int element, int direction,
14091                                 int player_index_bit)
14092 {
14093   struct ElementInfo *ei = &element_info[element];
14094   int direction_bit = MV_DIR_TO_BIT(direction);
14095   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14096   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14097                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14098
14099   Tile[x][y] = EL_ELEMENT_SNAPPING;
14100   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14101   MovDir[x][y] = direction;
14102   Store[x][y] = element;
14103   Store2[x][y] = player_index_bit;
14104
14105   ResetGfxAnimation(x, y);
14106
14107   GfxElement[x][y] = element;
14108   GfxAction[x][y] = action;
14109   GfxDir[x][y] = direction;
14110   GfxFrame[x][y] = -1;
14111 }
14112
14113 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14114                                    int player_index_bit)
14115 {
14116   TestIfElementTouchesCustomElement(x, y);      // for empty space
14117
14118   if (level.finish_dig_collect)
14119   {
14120     int dig_side = MV_DIR_OPPOSITE(direction);
14121     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14122                         CE_PLAYER_COLLECTS_X);
14123
14124     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14125                                         player_index_bit, dig_side);
14126     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14127                                         player_index_bit, dig_side);
14128   }
14129 }
14130
14131 /*
14132   =============================================================================
14133   checkDiagonalPushing()
14134   -----------------------------------------------------------------------------
14135   check if diagonal input device direction results in pushing of object
14136   (by checking if the alternative direction is walkable, diggable, ...)
14137   =============================================================================
14138 */
14139
14140 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14141                                     int x, int y, int real_dx, int real_dy)
14142 {
14143   int jx, jy, dx, dy, xx, yy;
14144
14145   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14146     return TRUE;
14147
14148   // diagonal direction: check alternative direction
14149   jx = player->jx;
14150   jy = player->jy;
14151   dx = x - jx;
14152   dy = y - jy;
14153   xx = jx + (dx == 0 ? real_dx : 0);
14154   yy = jy + (dy == 0 ? real_dy : 0);
14155
14156   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14157 }
14158
14159 /*
14160   =============================================================================
14161   DigField()
14162   -----------------------------------------------------------------------------
14163   x, y:                 field next to player (non-diagonal) to try to dig to
14164   real_dx, real_dy:     direction as read from input device (can be diagonal)
14165   =============================================================================
14166 */
14167
14168 static int DigField(struct PlayerInfo *player,
14169                     int oldx, int oldy, int x, int y,
14170                     int real_dx, int real_dy, int mode)
14171 {
14172   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14173   boolean player_was_pushing = player->is_pushing;
14174   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14175   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14176   int jx = oldx, jy = oldy;
14177   int dx = x - jx, dy = y - jy;
14178   int nextx = x + dx, nexty = y + dy;
14179   int move_direction = (dx == -1 ? MV_LEFT  :
14180                         dx == +1 ? MV_RIGHT :
14181                         dy == -1 ? MV_UP    :
14182                         dy == +1 ? MV_DOWN  : MV_NONE);
14183   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14184   int dig_side = MV_DIR_OPPOSITE(move_direction);
14185   int old_element = Tile[jx][jy];
14186   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14187   int collect_count;
14188
14189   if (is_player)                // function can also be called by EL_PENGUIN
14190   {
14191     if (player->MovPos == 0)
14192     {
14193       player->is_digging = FALSE;
14194       player->is_collecting = FALSE;
14195     }
14196
14197     if (player->MovPos == 0)    // last pushing move finished
14198       player->is_pushing = FALSE;
14199
14200     if (mode == DF_NO_PUSH)     // player just stopped pushing
14201     {
14202       player->is_switching = FALSE;
14203       player->push_delay = -1;
14204
14205       return MP_NO_ACTION;
14206     }
14207   }
14208   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14209     old_element = Back[jx][jy];
14210
14211   // in case of element dropped at player position, check background
14212   else if (Back[jx][jy] != EL_EMPTY &&
14213            game.engine_version >= VERSION_IDENT(2,2,0,0))
14214     old_element = Back[jx][jy];
14215
14216   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14217     return MP_NO_ACTION;        // field has no opening in this direction
14218
14219   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14220     return MP_NO_ACTION;        // field has no opening in this direction
14221
14222   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14223   {
14224     SplashAcid(x, y);
14225
14226     Tile[jx][jy] = player->artwork_element;
14227     InitMovingField(jx, jy, MV_DOWN);
14228     Store[jx][jy] = EL_ACID;
14229     ContinueMoving(jx, jy);
14230     BuryPlayer(player);
14231
14232     return MP_DONT_RUN_INTO;
14233   }
14234
14235   if (player_can_move && DONT_RUN_INTO(element))
14236   {
14237     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14238
14239     return MP_DONT_RUN_INTO;
14240   }
14241
14242   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14243     return MP_NO_ACTION;
14244
14245   collect_count = element_info[element].collect_count_initial;
14246
14247   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14248     return MP_NO_ACTION;
14249
14250   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14251     player_can_move = player_can_move_or_snap;
14252
14253   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14254       game.engine_version >= VERSION_IDENT(2,2,0,0))
14255   {
14256     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14257                                player->index_bit, dig_side);
14258     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14259                                         player->index_bit, dig_side);
14260
14261     if (element == EL_DC_LANDMINE)
14262       Bang(x, y);
14263
14264     if (Tile[x][y] != element)          // field changed by snapping
14265       return MP_ACTION;
14266
14267     return MP_NO_ACTION;
14268   }
14269
14270   if (player->gravity && is_player && !player->is_auto_moving &&
14271       canFallDown(player) && move_direction != MV_DOWN &&
14272       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14273     return MP_NO_ACTION;        // player cannot walk here due to gravity
14274
14275   if (player_can_move &&
14276       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14277   {
14278     int sound_element = SND_ELEMENT(element);
14279     int sound_action = ACTION_WALKING;
14280
14281     if (IS_RND_GATE(element))
14282     {
14283       if (!player->key[RND_GATE_NR(element)])
14284         return MP_NO_ACTION;
14285     }
14286     else if (IS_RND_GATE_GRAY(element))
14287     {
14288       if (!player->key[RND_GATE_GRAY_NR(element)])
14289         return MP_NO_ACTION;
14290     }
14291     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14292     {
14293       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14294         return MP_NO_ACTION;
14295     }
14296     else if (element == EL_EXIT_OPEN ||
14297              element == EL_EM_EXIT_OPEN ||
14298              element == EL_EM_EXIT_OPENING ||
14299              element == EL_STEEL_EXIT_OPEN ||
14300              element == EL_EM_STEEL_EXIT_OPEN ||
14301              element == EL_EM_STEEL_EXIT_OPENING ||
14302              element == EL_SP_EXIT_OPEN ||
14303              element == EL_SP_EXIT_OPENING)
14304     {
14305       sound_action = ACTION_PASSING;    // player is passing exit
14306     }
14307     else if (element == EL_EMPTY)
14308     {
14309       sound_action = ACTION_MOVING;             // nothing to walk on
14310     }
14311
14312     // play sound from background or player, whatever is available
14313     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14314       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14315     else
14316       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14317   }
14318   else if (player_can_move &&
14319            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14320   {
14321     if (!ACCESS_FROM(element, opposite_direction))
14322       return MP_NO_ACTION;      // field not accessible from this direction
14323
14324     if (CAN_MOVE(element))      // only fixed elements can be passed!
14325       return MP_NO_ACTION;
14326
14327     if (IS_EM_GATE(element))
14328     {
14329       if (!player->key[EM_GATE_NR(element)])
14330         return MP_NO_ACTION;
14331     }
14332     else if (IS_EM_GATE_GRAY(element))
14333     {
14334       if (!player->key[EM_GATE_GRAY_NR(element)])
14335         return MP_NO_ACTION;
14336     }
14337     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14338     {
14339       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14340         return MP_NO_ACTION;
14341     }
14342     else if (IS_EMC_GATE(element))
14343     {
14344       if (!player->key[EMC_GATE_NR(element)])
14345         return MP_NO_ACTION;
14346     }
14347     else if (IS_EMC_GATE_GRAY(element))
14348     {
14349       if (!player->key[EMC_GATE_GRAY_NR(element)])
14350         return MP_NO_ACTION;
14351     }
14352     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14353     {
14354       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14355         return MP_NO_ACTION;
14356     }
14357     else if (element == EL_DC_GATE_WHITE ||
14358              element == EL_DC_GATE_WHITE_GRAY ||
14359              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14360     {
14361       if (player->num_white_keys == 0)
14362         return MP_NO_ACTION;
14363
14364       player->num_white_keys--;
14365     }
14366     else if (IS_SP_PORT(element))
14367     {
14368       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14369           element == EL_SP_GRAVITY_PORT_RIGHT ||
14370           element == EL_SP_GRAVITY_PORT_UP ||
14371           element == EL_SP_GRAVITY_PORT_DOWN)
14372         player->gravity = !player->gravity;
14373       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14374                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14375                element == EL_SP_GRAVITY_ON_PORT_UP ||
14376                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14377         player->gravity = TRUE;
14378       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14379                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14380                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14381                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14382         player->gravity = FALSE;
14383     }
14384
14385     // automatically move to the next field with double speed
14386     player->programmed_action = move_direction;
14387
14388     if (player->move_delay_reset_counter == 0)
14389     {
14390       player->move_delay_reset_counter = 2;     // two double speed steps
14391
14392       DOUBLE_PLAYER_SPEED(player);
14393     }
14394
14395     PlayLevelSoundAction(x, y, ACTION_PASSING);
14396   }
14397   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14398   {
14399     RemoveField(x, y);
14400
14401     if (mode != DF_SNAP)
14402     {
14403       GfxElement[x][y] = GFX_ELEMENT(element);
14404       player->is_digging = TRUE;
14405     }
14406
14407     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14408
14409     // use old behaviour for old levels (digging)
14410     if (!level.finish_dig_collect)
14411     {
14412       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14413                                           player->index_bit, dig_side);
14414
14415       // if digging triggered player relocation, finish digging tile
14416       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14417         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14418     }
14419
14420     if (mode == DF_SNAP)
14421     {
14422       if (level.block_snap_field)
14423         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14424       else
14425         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14426
14427       // use old behaviour for old levels (snapping)
14428       if (!level.finish_dig_collect)
14429         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14430                                             player->index_bit, dig_side);
14431     }
14432   }
14433   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14434   {
14435     RemoveField(x, y);
14436
14437     if (is_player && mode != DF_SNAP)
14438     {
14439       GfxElement[x][y] = element;
14440       player->is_collecting = TRUE;
14441     }
14442
14443     if (element == EL_SPEED_PILL)
14444     {
14445       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14446     }
14447     else if (element == EL_EXTRA_TIME && level.time > 0)
14448     {
14449       TimeLeft += level.extra_time;
14450
14451       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14452
14453       DisplayGameControlValues();
14454     }
14455     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14456     {
14457       player->shield_normal_time_left += level.shield_normal_time;
14458       if (element == EL_SHIELD_DEADLY)
14459         player->shield_deadly_time_left += level.shield_deadly_time;
14460     }
14461     else if (element == EL_DYNAMITE ||
14462              element == EL_EM_DYNAMITE ||
14463              element == EL_SP_DISK_RED)
14464     {
14465       if (player->inventory_size < MAX_INVENTORY_SIZE)
14466         player->inventory_element[player->inventory_size++] = element;
14467
14468       DrawGameDoorValues();
14469     }
14470     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14471     {
14472       player->dynabomb_count++;
14473       player->dynabombs_left++;
14474     }
14475     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14476     {
14477       player->dynabomb_size++;
14478     }
14479     else if (element == EL_DYNABOMB_INCREASE_POWER)
14480     {
14481       player->dynabomb_xl = TRUE;
14482     }
14483     else if (IS_KEY(element))
14484     {
14485       player->key[KEY_NR(element)] = TRUE;
14486
14487       DrawGameDoorValues();
14488     }
14489     else if (element == EL_DC_KEY_WHITE)
14490     {
14491       player->num_white_keys++;
14492
14493       // display white keys?
14494       // DrawGameDoorValues();
14495     }
14496     else if (IS_ENVELOPE(element))
14497     {
14498       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14499
14500       if (!wait_for_snapping)
14501         player->show_envelope = element;
14502     }
14503     else if (element == EL_EMC_LENSES)
14504     {
14505       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14506
14507       RedrawAllInvisibleElementsForLenses();
14508     }
14509     else if (element == EL_EMC_MAGNIFIER)
14510     {
14511       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14512
14513       RedrawAllInvisibleElementsForMagnifier();
14514     }
14515     else if (IS_DROPPABLE(element) ||
14516              IS_THROWABLE(element))     // can be collected and dropped
14517     {
14518       int i;
14519
14520       if (collect_count == 0)
14521         player->inventory_infinite_element = element;
14522       else
14523         for (i = 0; i < collect_count; i++)
14524           if (player->inventory_size < MAX_INVENTORY_SIZE)
14525             player->inventory_element[player->inventory_size++] = element;
14526
14527       DrawGameDoorValues();
14528     }
14529     else if (collect_count > 0)
14530     {
14531       game.gems_still_needed -= collect_count;
14532       if (game.gems_still_needed < 0)
14533         game.gems_still_needed = 0;
14534
14535       game.snapshot.collected_item = TRUE;
14536
14537       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14538
14539       DisplayGameControlValues();
14540     }
14541
14542     RaiseScoreElement(element);
14543     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14544
14545     // use old behaviour for old levels (collecting)
14546     if (!level.finish_dig_collect && is_player)
14547     {
14548       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14549                                           player->index_bit, dig_side);
14550
14551       // if collecting triggered player relocation, finish collecting tile
14552       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14553         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14554     }
14555
14556     if (mode == DF_SNAP)
14557     {
14558       if (level.block_snap_field)
14559         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14560       else
14561         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14562
14563       // use old behaviour for old levels (snapping)
14564       if (!level.finish_dig_collect)
14565         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14566                                             player->index_bit, dig_side);
14567     }
14568   }
14569   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14570   {
14571     if (mode == DF_SNAP && element != EL_BD_ROCK)
14572       return MP_NO_ACTION;
14573
14574     if (CAN_FALL(element) && dy)
14575       return MP_NO_ACTION;
14576
14577     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14578         !(element == EL_SPRING && level.use_spring_bug))
14579       return MP_NO_ACTION;
14580
14581     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14582         ((move_direction & MV_VERTICAL &&
14583           ((element_info[element].move_pattern & MV_LEFT &&
14584             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14585            (element_info[element].move_pattern & MV_RIGHT &&
14586             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14587          (move_direction & MV_HORIZONTAL &&
14588           ((element_info[element].move_pattern & MV_UP &&
14589             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14590            (element_info[element].move_pattern & MV_DOWN &&
14591             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14592       return MP_NO_ACTION;
14593
14594     // do not push elements already moving away faster than player
14595     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14596         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14597       return MP_NO_ACTION;
14598
14599     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14600     {
14601       if (player->push_delay_value == -1 || !player_was_pushing)
14602         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14603     }
14604     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14605     {
14606       if (player->push_delay_value == -1)
14607         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14608     }
14609     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14610     {
14611       if (!player->is_pushing)
14612         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14613     }
14614
14615     player->is_pushing = TRUE;
14616     player->is_active = TRUE;
14617
14618     if (!(IN_LEV_FIELD(nextx, nexty) &&
14619           (IS_FREE(nextx, nexty) ||
14620            (IS_SB_ELEMENT(element) &&
14621             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14622            (IS_CUSTOM_ELEMENT(element) &&
14623             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14624       return MP_NO_ACTION;
14625
14626     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14627       return MP_NO_ACTION;
14628
14629     if (player->push_delay == -1)       // new pushing; restart delay
14630       player->push_delay = 0;
14631
14632     if (player->push_delay < player->push_delay_value &&
14633         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14634         element != EL_SPRING && element != EL_BALLOON)
14635     {
14636       // make sure that there is no move delay before next try to push
14637       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14638         player->move_delay = 0;
14639
14640       return MP_NO_ACTION;
14641     }
14642
14643     if (IS_CUSTOM_ELEMENT(element) &&
14644         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14645     {
14646       if (!DigFieldByCE(nextx, nexty, element))
14647         return MP_NO_ACTION;
14648     }
14649
14650     if (IS_SB_ELEMENT(element))
14651     {
14652       boolean sokoban_task_solved = FALSE;
14653
14654       if (element == EL_SOKOBAN_FIELD_FULL)
14655       {
14656         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14657
14658         IncrementSokobanFieldsNeeded();
14659         IncrementSokobanObjectsNeeded();
14660       }
14661
14662       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14663       {
14664         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14665
14666         DecrementSokobanFieldsNeeded();
14667         DecrementSokobanObjectsNeeded();
14668
14669         // sokoban object was pushed from empty field to sokoban field
14670         if (Back[x][y] == EL_EMPTY)
14671           sokoban_task_solved = TRUE;
14672       }
14673
14674       Tile[x][y] = EL_SOKOBAN_OBJECT;
14675
14676       if (Back[x][y] == Back[nextx][nexty])
14677         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14678       else if (Back[x][y] != 0)
14679         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14680                                     ACTION_EMPTYING);
14681       else
14682         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14683                                     ACTION_FILLING);
14684
14685       if (sokoban_task_solved &&
14686           game.sokoban_fields_still_needed == 0 &&
14687           game.sokoban_objects_still_needed == 0 &&
14688           level.auto_exit_sokoban)
14689       {
14690         game.players_still_needed = 0;
14691
14692         LevelSolved();
14693
14694         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14695       }
14696     }
14697     else
14698       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14699
14700     InitMovingField(x, y, move_direction);
14701     GfxAction[x][y] = ACTION_PUSHING;
14702
14703     if (mode == DF_SNAP)
14704       ContinueMoving(x, y);
14705     else
14706       MovPos[x][y] = (dx != 0 ? dx : dy);
14707
14708     Pushed[x][y] = TRUE;
14709     Pushed[nextx][nexty] = TRUE;
14710
14711     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14712       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14713     else
14714       player->push_delay_value = -1;    // get new value later
14715
14716     // check for element change _after_ element has been pushed
14717     if (game.use_change_when_pushing_bug)
14718     {
14719       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14720                                  player->index_bit, dig_side);
14721       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14722                                           player->index_bit, dig_side);
14723     }
14724   }
14725   else if (IS_SWITCHABLE(element))
14726   {
14727     if (PLAYER_SWITCHING(player, x, y))
14728     {
14729       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14730                                           player->index_bit, dig_side);
14731
14732       return MP_ACTION;
14733     }
14734
14735     player->is_switching = TRUE;
14736     player->switch_x = x;
14737     player->switch_y = y;
14738
14739     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14740
14741     if (element == EL_ROBOT_WHEEL)
14742     {
14743       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14744
14745       game.robot_wheel_x = x;
14746       game.robot_wheel_y = y;
14747       game.robot_wheel_active = TRUE;
14748
14749       TEST_DrawLevelField(x, y);
14750     }
14751     else if (element == EL_SP_TERMINAL)
14752     {
14753       int xx, yy;
14754
14755       SCAN_PLAYFIELD(xx, yy)
14756       {
14757         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14758         {
14759           Bang(xx, yy);
14760         }
14761         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14762         {
14763           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14764
14765           ResetGfxAnimation(xx, yy);
14766           TEST_DrawLevelField(xx, yy);
14767         }
14768       }
14769     }
14770     else if (IS_BELT_SWITCH(element))
14771     {
14772       ToggleBeltSwitch(x, y);
14773     }
14774     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14775              element == EL_SWITCHGATE_SWITCH_DOWN ||
14776              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14777              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14778     {
14779       ToggleSwitchgateSwitch(x, y);
14780     }
14781     else if (element == EL_LIGHT_SWITCH ||
14782              element == EL_LIGHT_SWITCH_ACTIVE)
14783     {
14784       ToggleLightSwitch(x, y);
14785     }
14786     else if (element == EL_TIMEGATE_SWITCH ||
14787              element == EL_DC_TIMEGATE_SWITCH)
14788     {
14789       ActivateTimegateSwitch(x, y);
14790     }
14791     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14792              element == EL_BALLOON_SWITCH_RIGHT ||
14793              element == EL_BALLOON_SWITCH_UP    ||
14794              element == EL_BALLOON_SWITCH_DOWN  ||
14795              element == EL_BALLOON_SWITCH_NONE  ||
14796              element == EL_BALLOON_SWITCH_ANY)
14797     {
14798       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14799                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14800                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14801                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14802                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14803                              move_direction);
14804     }
14805     else if (element == EL_LAMP)
14806     {
14807       Tile[x][y] = EL_LAMP_ACTIVE;
14808       game.lights_still_needed--;
14809
14810       ResetGfxAnimation(x, y);
14811       TEST_DrawLevelField(x, y);
14812     }
14813     else if (element == EL_TIME_ORB_FULL)
14814     {
14815       Tile[x][y] = EL_TIME_ORB_EMPTY;
14816
14817       if (level.time > 0 || level.use_time_orb_bug)
14818       {
14819         TimeLeft += level.time_orb_time;
14820         game.no_time_limit = FALSE;
14821
14822         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14823
14824         DisplayGameControlValues();
14825       }
14826
14827       ResetGfxAnimation(x, y);
14828       TEST_DrawLevelField(x, y);
14829     }
14830     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14831              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14832     {
14833       int xx, yy;
14834
14835       game.ball_active = !game.ball_active;
14836
14837       SCAN_PLAYFIELD(xx, yy)
14838       {
14839         int e = Tile[xx][yy];
14840
14841         if (game.ball_active)
14842         {
14843           if (e == EL_EMC_MAGIC_BALL)
14844             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14845           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14846             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14847         }
14848         else
14849         {
14850           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14851             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14852           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14853             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14854         }
14855       }
14856     }
14857
14858     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14859                                         player->index_bit, dig_side);
14860
14861     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14862                                         player->index_bit, dig_side);
14863
14864     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14865                                         player->index_bit, dig_side);
14866
14867     return MP_ACTION;
14868   }
14869   else
14870   {
14871     if (!PLAYER_SWITCHING(player, x, y))
14872     {
14873       player->is_switching = TRUE;
14874       player->switch_x = x;
14875       player->switch_y = y;
14876
14877       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14878                                  player->index_bit, dig_side);
14879       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14880                                           player->index_bit, dig_side);
14881
14882       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14883                                  player->index_bit, dig_side);
14884       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14885                                           player->index_bit, dig_side);
14886     }
14887
14888     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14889                                player->index_bit, dig_side);
14890     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14891                                         player->index_bit, dig_side);
14892
14893     return MP_NO_ACTION;
14894   }
14895
14896   player->push_delay = -1;
14897
14898   if (is_player)                // function can also be called by EL_PENGUIN
14899   {
14900     if (Tile[x][y] != element)          // really digged/collected something
14901     {
14902       player->is_collecting = !player->is_digging;
14903       player->is_active = TRUE;
14904
14905       player->last_removed_element = element;
14906     }
14907   }
14908
14909   return MP_MOVING;
14910 }
14911
14912 static boolean DigFieldByCE(int x, int y, int digging_element)
14913 {
14914   int element = Tile[x][y];
14915
14916   if (!IS_FREE(x, y))
14917   {
14918     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14919                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14920                   ACTION_BREAKING);
14921
14922     // no element can dig solid indestructible elements
14923     if (IS_INDESTRUCTIBLE(element) &&
14924         !IS_DIGGABLE(element) &&
14925         !IS_COLLECTIBLE(element))
14926       return FALSE;
14927
14928     if (AmoebaNr[x][y] &&
14929         (element == EL_AMOEBA_FULL ||
14930          element == EL_BD_AMOEBA ||
14931          element == EL_AMOEBA_GROWING))
14932     {
14933       AmoebaCnt[AmoebaNr[x][y]]--;
14934       AmoebaCnt2[AmoebaNr[x][y]]--;
14935     }
14936
14937     if (IS_MOVING(x, y))
14938       RemoveMovingField(x, y);
14939     else
14940     {
14941       RemoveField(x, y);
14942       TEST_DrawLevelField(x, y);
14943     }
14944
14945     // if digged element was about to explode, prevent the explosion
14946     ExplodeField[x][y] = EX_TYPE_NONE;
14947
14948     PlayLevelSoundAction(x, y, action);
14949   }
14950
14951   Store[x][y] = EL_EMPTY;
14952
14953   // this makes it possible to leave the removed element again
14954   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14955     Store[x][y] = element;
14956
14957   return TRUE;
14958 }
14959
14960 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14961 {
14962   int jx = player->jx, jy = player->jy;
14963   int x = jx + dx, y = jy + dy;
14964   int snap_direction = (dx == -1 ? MV_LEFT  :
14965                         dx == +1 ? MV_RIGHT :
14966                         dy == -1 ? MV_UP    :
14967                         dy == +1 ? MV_DOWN  : MV_NONE);
14968   boolean can_continue_snapping = (level.continuous_snapping &&
14969                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14970
14971   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14972     return FALSE;
14973
14974   if (!player->active || !IN_LEV_FIELD(x, y))
14975     return FALSE;
14976
14977   if (dx && dy)
14978     return FALSE;
14979
14980   if (!dx && !dy)
14981   {
14982     if (player->MovPos == 0)
14983       player->is_pushing = FALSE;
14984
14985     player->is_snapping = FALSE;
14986
14987     if (player->MovPos == 0)
14988     {
14989       player->is_moving = FALSE;
14990       player->is_digging = FALSE;
14991       player->is_collecting = FALSE;
14992     }
14993
14994     return FALSE;
14995   }
14996
14997   // prevent snapping with already pressed snap key when not allowed
14998   if (player->is_snapping && !can_continue_snapping)
14999     return FALSE;
15000
15001   player->MovDir = snap_direction;
15002
15003   if (player->MovPos == 0)
15004   {
15005     player->is_moving = FALSE;
15006     player->is_digging = FALSE;
15007     player->is_collecting = FALSE;
15008   }
15009
15010   player->is_dropping = FALSE;
15011   player->is_dropping_pressed = FALSE;
15012   player->drop_pressed_delay = 0;
15013
15014   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15015     return FALSE;
15016
15017   player->is_snapping = TRUE;
15018   player->is_active = TRUE;
15019
15020   if (player->MovPos == 0)
15021   {
15022     player->is_moving = FALSE;
15023     player->is_digging = FALSE;
15024     player->is_collecting = FALSE;
15025   }
15026
15027   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15028     TEST_DrawLevelField(player->last_jx, player->last_jy);
15029
15030   TEST_DrawLevelField(x, y);
15031
15032   return TRUE;
15033 }
15034
15035 static boolean DropElement(struct PlayerInfo *player)
15036 {
15037   int old_element, new_element;
15038   int dropx = player->jx, dropy = player->jy;
15039   int drop_direction = player->MovDir;
15040   int drop_side = drop_direction;
15041   int drop_element = get_next_dropped_element(player);
15042
15043   /* do not drop an element on top of another element; when holding drop key
15044      pressed without moving, dropped element must move away before the next
15045      element can be dropped (this is especially important if the next element
15046      is dynamite, which can be placed on background for historical reasons) */
15047   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15048     return MP_ACTION;
15049
15050   if (IS_THROWABLE(drop_element))
15051   {
15052     dropx += GET_DX_FROM_DIR(drop_direction);
15053     dropy += GET_DY_FROM_DIR(drop_direction);
15054
15055     if (!IN_LEV_FIELD(dropx, dropy))
15056       return FALSE;
15057   }
15058
15059   old_element = Tile[dropx][dropy];     // old element at dropping position
15060   new_element = drop_element;           // default: no change when dropping
15061
15062   // check if player is active, not moving and ready to drop
15063   if (!player->active || player->MovPos || player->drop_delay > 0)
15064     return FALSE;
15065
15066   // check if player has anything that can be dropped
15067   if (new_element == EL_UNDEFINED)
15068     return FALSE;
15069
15070   // only set if player has anything that can be dropped
15071   player->is_dropping_pressed = TRUE;
15072
15073   // check if drop key was pressed long enough for EM style dynamite
15074   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15075     return FALSE;
15076
15077   // check if anything can be dropped at the current position
15078   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15079     return FALSE;
15080
15081   // collected custom elements can only be dropped on empty fields
15082   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15083     return FALSE;
15084
15085   if (old_element != EL_EMPTY)
15086     Back[dropx][dropy] = old_element;   // store old element on this field
15087
15088   ResetGfxAnimation(dropx, dropy);
15089   ResetRandomAnimationValue(dropx, dropy);
15090
15091   if (player->inventory_size > 0 ||
15092       player->inventory_infinite_element != EL_UNDEFINED)
15093   {
15094     if (player->inventory_size > 0)
15095     {
15096       player->inventory_size--;
15097
15098       DrawGameDoorValues();
15099
15100       if (new_element == EL_DYNAMITE)
15101         new_element = EL_DYNAMITE_ACTIVE;
15102       else if (new_element == EL_EM_DYNAMITE)
15103         new_element = EL_EM_DYNAMITE_ACTIVE;
15104       else if (new_element == EL_SP_DISK_RED)
15105         new_element = EL_SP_DISK_RED_ACTIVE;
15106     }
15107
15108     Tile[dropx][dropy] = new_element;
15109
15110     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15111       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15112                           el2img(Tile[dropx][dropy]), 0);
15113
15114     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15115
15116     // needed if previous element just changed to "empty" in the last frame
15117     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15118
15119     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15120                                player->index_bit, drop_side);
15121     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15122                                         CE_PLAYER_DROPS_X,
15123                                         player->index_bit, drop_side);
15124
15125     TestIfElementTouchesCustomElement(dropx, dropy);
15126   }
15127   else          // player is dropping a dyna bomb
15128   {
15129     player->dynabombs_left--;
15130
15131     Tile[dropx][dropy] = new_element;
15132
15133     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15134       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15135                           el2img(Tile[dropx][dropy]), 0);
15136
15137     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15138   }
15139
15140   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15141     InitField_WithBug1(dropx, dropy, FALSE);
15142
15143   new_element = Tile[dropx][dropy];     // element might have changed
15144
15145   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15146       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15147   {
15148     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15149       MovDir[dropx][dropy] = drop_direction;
15150
15151     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15152
15153     // do not cause impact style collision by dropping elements that can fall
15154     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15155   }
15156
15157   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15158   player->is_dropping = TRUE;
15159
15160   player->drop_pressed_delay = 0;
15161   player->is_dropping_pressed = FALSE;
15162
15163   player->drop_x = dropx;
15164   player->drop_y = dropy;
15165
15166   return TRUE;
15167 }
15168
15169 // ----------------------------------------------------------------------------
15170 // game sound playing functions
15171 // ----------------------------------------------------------------------------
15172
15173 static int *loop_sound_frame = NULL;
15174 static int *loop_sound_volume = NULL;
15175
15176 void InitPlayLevelSound(void)
15177 {
15178   int num_sounds = getSoundListSize();
15179
15180   checked_free(loop_sound_frame);
15181   checked_free(loop_sound_volume);
15182
15183   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15184   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15185 }
15186
15187 static void PlayLevelSound(int x, int y, int nr)
15188 {
15189   int sx = SCREENX(x), sy = SCREENY(y);
15190   int volume, stereo_position;
15191   int max_distance = 8;
15192   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15193
15194   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15195       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15196     return;
15197
15198   if (!IN_LEV_FIELD(x, y) ||
15199       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15200       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15201     return;
15202
15203   volume = SOUND_MAX_VOLUME;
15204
15205   if (!IN_SCR_FIELD(sx, sy))
15206   {
15207     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15208     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15209
15210     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15211   }
15212
15213   stereo_position = (SOUND_MAX_LEFT +
15214                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15215                      (SCR_FIELDX + 2 * max_distance));
15216
15217   if (IS_LOOP_SOUND(nr))
15218   {
15219     /* This assures that quieter loop sounds do not overwrite louder ones,
15220        while restarting sound volume comparison with each new game frame. */
15221
15222     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15223       return;
15224
15225     loop_sound_volume[nr] = volume;
15226     loop_sound_frame[nr] = FrameCounter;
15227   }
15228
15229   PlaySoundExt(nr, volume, stereo_position, type);
15230 }
15231
15232 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15233 {
15234   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15235                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15236                  y < LEVELY(BY1) ? LEVELY(BY1) :
15237                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15238                  sound_action);
15239 }
15240
15241 static void PlayLevelSoundAction(int x, int y, int action)
15242 {
15243   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15244 }
15245
15246 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15247 {
15248   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15249
15250   if (sound_effect != SND_UNDEFINED)
15251     PlayLevelSound(x, y, sound_effect);
15252 }
15253
15254 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15255                                               int action)
15256 {
15257   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15258
15259   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15260     PlayLevelSound(x, y, sound_effect);
15261 }
15262
15263 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15264 {
15265   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15266
15267   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15268     PlayLevelSound(x, y, sound_effect);
15269 }
15270
15271 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15272 {
15273   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15274
15275   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15276     StopSound(sound_effect);
15277 }
15278
15279 static int getLevelMusicNr(void)
15280 {
15281   if (levelset.music[level_nr] != MUS_UNDEFINED)
15282     return levelset.music[level_nr];            // from config file
15283   else
15284     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15285 }
15286
15287 static void FadeLevelSounds(void)
15288 {
15289   FadeSounds();
15290 }
15291
15292 static void FadeLevelMusic(void)
15293 {
15294   int music_nr = getLevelMusicNr();
15295   char *curr_music = getCurrentlyPlayingMusicFilename();
15296   char *next_music = getMusicInfoEntryFilename(music_nr);
15297
15298   if (!strEqual(curr_music, next_music))
15299     FadeMusic();
15300 }
15301
15302 void FadeLevelSoundsAndMusic(void)
15303 {
15304   FadeLevelSounds();
15305   FadeLevelMusic();
15306 }
15307
15308 static void PlayLevelMusic(void)
15309 {
15310   int music_nr = getLevelMusicNr();
15311   char *curr_music = getCurrentlyPlayingMusicFilename();
15312   char *next_music = getMusicInfoEntryFilename(music_nr);
15313
15314   if (!strEqual(curr_music, next_music))
15315     PlayMusicLoop(music_nr);
15316 }
15317
15318 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15319 {
15320   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15321   int offset = 0;
15322   int x = xx - offset;
15323   int y = yy - offset;
15324
15325   switch (sample)
15326   {
15327     case SOUND_blank:
15328       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15329       break;
15330
15331     case SOUND_roll:
15332       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15333       break;
15334
15335     case SOUND_stone:
15336       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15337       break;
15338
15339     case SOUND_nut:
15340       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15341       break;
15342
15343     case SOUND_crack:
15344       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15345       break;
15346
15347     case SOUND_bug:
15348       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15349       break;
15350
15351     case SOUND_tank:
15352       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15353       break;
15354
15355     case SOUND_android_clone:
15356       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15357       break;
15358
15359     case SOUND_android_move:
15360       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15361       break;
15362
15363     case SOUND_spring:
15364       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15365       break;
15366
15367     case SOUND_slurp:
15368       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15369       break;
15370
15371     case SOUND_eater:
15372       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15373       break;
15374
15375     case SOUND_eater_eat:
15376       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15377       break;
15378
15379     case SOUND_alien:
15380       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15381       break;
15382
15383     case SOUND_collect:
15384       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15385       break;
15386
15387     case SOUND_diamond:
15388       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15389       break;
15390
15391     case SOUND_squash:
15392       // !!! CHECK THIS !!!
15393 #if 1
15394       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15395 #else
15396       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15397 #endif
15398       break;
15399
15400     case SOUND_wonderfall:
15401       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15402       break;
15403
15404     case SOUND_drip:
15405       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15406       break;
15407
15408     case SOUND_push:
15409       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15410       break;
15411
15412     case SOUND_dirt:
15413       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15414       break;
15415
15416     case SOUND_acid:
15417       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15418       break;
15419
15420     case SOUND_ball:
15421       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15422       break;
15423
15424     case SOUND_slide:
15425       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15426       break;
15427
15428     case SOUND_wonder:
15429       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15430       break;
15431
15432     case SOUND_door:
15433       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15434       break;
15435
15436     case SOUND_exit_open:
15437       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15438       break;
15439
15440     case SOUND_exit_leave:
15441       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15442       break;
15443
15444     case SOUND_dynamite:
15445       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15446       break;
15447
15448     case SOUND_tick:
15449       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15450       break;
15451
15452     case SOUND_press:
15453       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15454       break;
15455
15456     case SOUND_wheel:
15457       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15458       break;
15459
15460     case SOUND_boom:
15461       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15462       break;
15463
15464     case SOUND_die:
15465       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15466       break;
15467
15468     case SOUND_time:
15469       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15470       break;
15471
15472     default:
15473       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15474       break;
15475   }
15476 }
15477
15478 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15479 {
15480   int element = map_element_SP_to_RND(element_sp);
15481   int action = map_action_SP_to_RND(action_sp);
15482   int offset = (setup.sp_show_border_elements ? 0 : 1);
15483   int x = xx - offset;
15484   int y = yy - offset;
15485
15486   PlayLevelSoundElementAction(x, y, element, action);
15487 }
15488
15489 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15490 {
15491   int element = map_element_MM_to_RND(element_mm);
15492   int action = map_action_MM_to_RND(action_mm);
15493   int offset = 0;
15494   int x = xx - offset;
15495   int y = yy - offset;
15496
15497   if (!IS_MM_ELEMENT(element))
15498     element = EL_MM_DEFAULT;
15499
15500   PlayLevelSoundElementAction(x, y, element, action);
15501 }
15502
15503 void PlaySound_MM(int sound_mm)
15504 {
15505   int sound = map_sound_MM_to_RND(sound_mm);
15506
15507   if (sound == SND_UNDEFINED)
15508     return;
15509
15510   PlaySound(sound);
15511 }
15512
15513 void PlaySoundLoop_MM(int sound_mm)
15514 {
15515   int sound = map_sound_MM_to_RND(sound_mm);
15516
15517   if (sound == SND_UNDEFINED)
15518     return;
15519
15520   PlaySoundLoop(sound);
15521 }
15522
15523 void StopSound_MM(int sound_mm)
15524 {
15525   int sound = map_sound_MM_to_RND(sound_mm);
15526
15527   if (sound == SND_UNDEFINED)
15528     return;
15529
15530   StopSound(sound);
15531 }
15532
15533 void RaiseScore(int value)
15534 {
15535   game.score += value;
15536
15537   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15538
15539   DisplayGameControlValues();
15540 }
15541
15542 void RaiseScoreElement(int element)
15543 {
15544   switch (element)
15545   {
15546     case EL_EMERALD:
15547     case EL_BD_DIAMOND:
15548     case EL_EMERALD_YELLOW:
15549     case EL_EMERALD_RED:
15550     case EL_EMERALD_PURPLE:
15551     case EL_SP_INFOTRON:
15552       RaiseScore(level.score[SC_EMERALD]);
15553       break;
15554     case EL_DIAMOND:
15555       RaiseScore(level.score[SC_DIAMOND]);
15556       break;
15557     case EL_CRYSTAL:
15558       RaiseScore(level.score[SC_CRYSTAL]);
15559       break;
15560     case EL_PEARL:
15561       RaiseScore(level.score[SC_PEARL]);
15562       break;
15563     case EL_BUG:
15564     case EL_BD_BUTTERFLY:
15565     case EL_SP_ELECTRON:
15566       RaiseScore(level.score[SC_BUG]);
15567       break;
15568     case EL_SPACESHIP:
15569     case EL_BD_FIREFLY:
15570     case EL_SP_SNIKSNAK:
15571       RaiseScore(level.score[SC_SPACESHIP]);
15572       break;
15573     case EL_YAMYAM:
15574     case EL_DARK_YAMYAM:
15575       RaiseScore(level.score[SC_YAMYAM]);
15576       break;
15577     case EL_ROBOT:
15578       RaiseScore(level.score[SC_ROBOT]);
15579       break;
15580     case EL_PACMAN:
15581       RaiseScore(level.score[SC_PACMAN]);
15582       break;
15583     case EL_NUT:
15584       RaiseScore(level.score[SC_NUT]);
15585       break;
15586     case EL_DYNAMITE:
15587     case EL_EM_DYNAMITE:
15588     case EL_SP_DISK_RED:
15589     case EL_DYNABOMB_INCREASE_NUMBER:
15590     case EL_DYNABOMB_INCREASE_SIZE:
15591     case EL_DYNABOMB_INCREASE_POWER:
15592       RaiseScore(level.score[SC_DYNAMITE]);
15593       break;
15594     case EL_SHIELD_NORMAL:
15595     case EL_SHIELD_DEADLY:
15596       RaiseScore(level.score[SC_SHIELD]);
15597       break;
15598     case EL_EXTRA_TIME:
15599       RaiseScore(level.extra_time_score);
15600       break;
15601     case EL_KEY_1:
15602     case EL_KEY_2:
15603     case EL_KEY_3:
15604     case EL_KEY_4:
15605     case EL_EM_KEY_1:
15606     case EL_EM_KEY_2:
15607     case EL_EM_KEY_3:
15608     case EL_EM_KEY_4:
15609     case EL_EMC_KEY_5:
15610     case EL_EMC_KEY_6:
15611     case EL_EMC_KEY_7:
15612     case EL_EMC_KEY_8:
15613     case EL_DC_KEY_WHITE:
15614       RaiseScore(level.score[SC_KEY]);
15615       break;
15616     default:
15617       RaiseScore(element_info[element].collect_score);
15618       break;
15619   }
15620 }
15621
15622 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15623 {
15624   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15625   {
15626     if (!quick_quit)
15627     {
15628       // prevent short reactivation of overlay buttons while closing door
15629       SetOverlayActive(FALSE);
15630
15631       // door may still be open due to skipped or envelope style request
15632       CloseDoor(DOOR_CLOSE_1);
15633     }
15634
15635     if (network.enabled)
15636       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15637     else
15638     {
15639       if (quick_quit)
15640         FadeSkipNextFadeIn();
15641
15642       SetGameStatus(GAME_MODE_MAIN);
15643
15644       DrawMainMenu();
15645     }
15646   }
15647   else          // continue playing the game
15648   {
15649     if (tape.playing && tape.deactivate_display)
15650       TapeDeactivateDisplayOff(TRUE);
15651
15652     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15653
15654     if (tape.playing && tape.deactivate_display)
15655       TapeDeactivateDisplayOn();
15656   }
15657 }
15658
15659 void RequestQuitGame(boolean escape_key_pressed)
15660 {
15661   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15662   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15663                         level_editor_test_game);
15664   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15665                           quick_quit);
15666
15667   RequestQuitGameExt(skip_request, quick_quit,
15668                      "Do you really want to quit the game?");
15669 }
15670
15671 void RequestRestartGame(char *message)
15672 {
15673   game.restart_game_message = NULL;
15674
15675   boolean has_started_game = hasStartedNetworkGame();
15676   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15677
15678   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15679   {
15680     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15681   }
15682   else
15683   {
15684     // needed in case of envelope request to close game panel
15685     CloseDoor(DOOR_CLOSE_1);
15686
15687     SetGameStatus(GAME_MODE_MAIN);
15688
15689     DrawMainMenu();
15690   }
15691 }
15692
15693 void CheckGameOver(void)
15694 {
15695   static boolean last_game_over = FALSE;
15696   static int game_over_delay = 0;
15697   int game_over_delay_value = 50;
15698   boolean game_over = checkGameFailed();
15699
15700   // do not handle game over if request dialog is already active
15701   if (game.request_active)
15702     return;
15703
15704   // do not ask to play again if game was never actually played
15705   if (!game.GamePlayed)
15706     return;
15707
15708   if (!game_over)
15709   {
15710     last_game_over = FALSE;
15711     game_over_delay = game_over_delay_value;
15712
15713     return;
15714   }
15715
15716   if (game_over_delay > 0)
15717   {
15718     game_over_delay--;
15719
15720     return;
15721   }
15722
15723   if (last_game_over != game_over)
15724     game.restart_game_message = (hasStartedNetworkGame() ?
15725                                  "Game over! Play it again?" :
15726                                  "Game over!");
15727
15728   last_game_over = game_over;
15729 }
15730
15731 boolean checkGameSolved(void)
15732 {
15733   // set for all game engines if level was solved
15734   return game.LevelSolved_GameEnd;
15735 }
15736
15737 boolean checkGameFailed(void)
15738 {
15739   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15740     return (game_em.game_over && !game_em.level_solved);
15741   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15742     return (game_sp.game_over && !game_sp.level_solved);
15743   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15744     return (game_mm.game_over && !game_mm.level_solved);
15745   else                          // GAME_ENGINE_TYPE_RND
15746     return (game.GameOver && !game.LevelSolved);
15747 }
15748
15749 boolean checkGameEnded(void)
15750 {
15751   return (checkGameSolved() || checkGameFailed());
15752 }
15753
15754
15755 // ----------------------------------------------------------------------------
15756 // random generator functions
15757 // ----------------------------------------------------------------------------
15758
15759 unsigned int InitEngineRandom_RND(int seed)
15760 {
15761   game.num_random_calls = 0;
15762
15763   return InitEngineRandom(seed);
15764 }
15765
15766 unsigned int RND(int max)
15767 {
15768   if (max > 0)
15769   {
15770     game.num_random_calls++;
15771
15772     return GetEngineRandom(max);
15773   }
15774
15775   return 0;
15776 }
15777
15778
15779 // ----------------------------------------------------------------------------
15780 // game engine snapshot handling functions
15781 // ----------------------------------------------------------------------------
15782
15783 struct EngineSnapshotInfo
15784 {
15785   // runtime values for custom element collect score
15786   int collect_score[NUM_CUSTOM_ELEMENTS];
15787
15788   // runtime values for group element choice position
15789   int choice_pos[NUM_GROUP_ELEMENTS];
15790
15791   // runtime values for belt position animations
15792   int belt_graphic[4][NUM_BELT_PARTS];
15793   int belt_anim_mode[4][NUM_BELT_PARTS];
15794 };
15795
15796 static struct EngineSnapshotInfo engine_snapshot_rnd;
15797 static char *snapshot_level_identifier = NULL;
15798 static int snapshot_level_nr = -1;
15799
15800 static void SaveEngineSnapshotValues_RND(void)
15801 {
15802   static int belt_base_active_element[4] =
15803   {
15804     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15805     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15806     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15807     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15808   };
15809   int i, j;
15810
15811   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15812   {
15813     int element = EL_CUSTOM_START + i;
15814
15815     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15816   }
15817
15818   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15819   {
15820     int element = EL_GROUP_START + i;
15821
15822     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15823   }
15824
15825   for (i = 0; i < 4; i++)
15826   {
15827     for (j = 0; j < NUM_BELT_PARTS; j++)
15828     {
15829       int element = belt_base_active_element[i] + j;
15830       int graphic = el2img(element);
15831       int anim_mode = graphic_info[graphic].anim_mode;
15832
15833       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15834       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15835     }
15836   }
15837 }
15838
15839 static void LoadEngineSnapshotValues_RND(void)
15840 {
15841   unsigned int num_random_calls = game.num_random_calls;
15842   int i, j;
15843
15844   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15845   {
15846     int element = EL_CUSTOM_START + i;
15847
15848     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15849   }
15850
15851   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15852   {
15853     int element = EL_GROUP_START + i;
15854
15855     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15856   }
15857
15858   for (i = 0; i < 4; i++)
15859   {
15860     for (j = 0; j < NUM_BELT_PARTS; j++)
15861     {
15862       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15863       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15864
15865       graphic_info[graphic].anim_mode = anim_mode;
15866     }
15867   }
15868
15869   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15870   {
15871     InitRND(tape.random_seed);
15872     for (i = 0; i < num_random_calls; i++)
15873       RND(1);
15874   }
15875
15876   if (game.num_random_calls != num_random_calls)
15877   {
15878     Error("number of random calls out of sync");
15879     Error("number of random calls should be %d", num_random_calls);
15880     Error("number of random calls is %d", game.num_random_calls);
15881
15882     Fail("this should not happen -- please debug");
15883   }
15884 }
15885
15886 void FreeEngineSnapshotSingle(void)
15887 {
15888   FreeSnapshotSingle();
15889
15890   setString(&snapshot_level_identifier, NULL);
15891   snapshot_level_nr = -1;
15892 }
15893
15894 void FreeEngineSnapshotList(void)
15895 {
15896   FreeSnapshotList();
15897 }
15898
15899 static ListNode *SaveEngineSnapshotBuffers(void)
15900 {
15901   ListNode *buffers = NULL;
15902
15903   // copy some special values to a structure better suited for the snapshot
15904
15905   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15906     SaveEngineSnapshotValues_RND();
15907   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15908     SaveEngineSnapshotValues_EM();
15909   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15910     SaveEngineSnapshotValues_SP(&buffers);
15911   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15912     SaveEngineSnapshotValues_MM(&buffers);
15913
15914   // save values stored in special snapshot structure
15915
15916   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15917     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15918   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15919     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15920   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15921     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15922   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15923     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15924
15925   // save further RND engine values
15926
15927   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15928   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15929   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15930
15931   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15932   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15933   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15934   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15935   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15936
15937   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15938   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15939   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15940
15941   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15942
15943   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15944   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15945
15946   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15947   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15948   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15949   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15950   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15951   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15952   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15953   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15954   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15955   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15956   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15957   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15958   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15959   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15960   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15961   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15962   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15963   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15964
15965   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15966   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15967
15968   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15969   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15970   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15971
15972   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15973   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15974
15975   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15976   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15977   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
15978   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15979   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15980   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15981
15982   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15983   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15984
15985 #if 0
15986   ListNode *node = engine_snapshot_list_rnd;
15987   int num_bytes = 0;
15988
15989   while (node != NULL)
15990   {
15991     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15992
15993     node = node->next;
15994   }
15995
15996   Debug("game:playing:SaveEngineSnapshotBuffers",
15997         "size of engine snapshot: %d bytes", num_bytes);
15998 #endif
15999
16000   return buffers;
16001 }
16002
16003 void SaveEngineSnapshotSingle(void)
16004 {
16005   ListNode *buffers = SaveEngineSnapshotBuffers();
16006
16007   // finally save all snapshot buffers to single snapshot
16008   SaveSnapshotSingle(buffers);
16009
16010   // save level identification information
16011   setString(&snapshot_level_identifier, leveldir_current->identifier);
16012   snapshot_level_nr = level_nr;
16013 }
16014
16015 boolean CheckSaveEngineSnapshotToList(void)
16016 {
16017   boolean save_snapshot =
16018     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16019      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16020       game.snapshot.changed_action) ||
16021      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16022       game.snapshot.collected_item));
16023
16024   game.snapshot.changed_action = FALSE;
16025   game.snapshot.collected_item = FALSE;
16026   game.snapshot.save_snapshot = save_snapshot;
16027
16028   return save_snapshot;
16029 }
16030
16031 void SaveEngineSnapshotToList(void)
16032 {
16033   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16034       tape.quick_resume)
16035     return;
16036
16037   ListNode *buffers = SaveEngineSnapshotBuffers();
16038
16039   // finally save all snapshot buffers to snapshot list
16040   SaveSnapshotToList(buffers);
16041 }
16042
16043 void SaveEngineSnapshotToListInitial(void)
16044 {
16045   FreeEngineSnapshotList();
16046
16047   SaveEngineSnapshotToList();
16048 }
16049
16050 static void LoadEngineSnapshotValues(void)
16051 {
16052   // restore special values from snapshot structure
16053
16054   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16055     LoadEngineSnapshotValues_RND();
16056   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16057     LoadEngineSnapshotValues_EM();
16058   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16059     LoadEngineSnapshotValues_SP();
16060   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16061     LoadEngineSnapshotValues_MM();
16062 }
16063
16064 void LoadEngineSnapshotSingle(void)
16065 {
16066   LoadSnapshotSingle();
16067
16068   LoadEngineSnapshotValues();
16069 }
16070
16071 static void LoadEngineSnapshot_Undo(int steps)
16072 {
16073   LoadSnapshotFromList_Older(steps);
16074
16075   LoadEngineSnapshotValues();
16076 }
16077
16078 static void LoadEngineSnapshot_Redo(int steps)
16079 {
16080   LoadSnapshotFromList_Newer(steps);
16081
16082   LoadEngineSnapshotValues();
16083 }
16084
16085 boolean CheckEngineSnapshotSingle(void)
16086 {
16087   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16088           snapshot_level_nr == level_nr);
16089 }
16090
16091 boolean CheckEngineSnapshotList(void)
16092 {
16093   return CheckSnapshotList();
16094 }
16095
16096
16097 // ---------- new game button stuff -------------------------------------------
16098
16099 static struct
16100 {
16101   int graphic;
16102   struct XY *pos;
16103   int gadget_id;
16104   boolean *setup_value;
16105   boolean allowed_on_tape;
16106   boolean is_touch_button;
16107   char *infotext;
16108 } gamebutton_info[NUM_GAME_BUTTONS] =
16109 {
16110   {
16111     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16112     GAME_CTRL_ID_STOP,                          NULL,
16113     TRUE, FALSE,                                "stop game"
16114   },
16115   {
16116     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16117     GAME_CTRL_ID_PAUSE,                         NULL,
16118     TRUE, FALSE,                                "pause game"
16119   },
16120   {
16121     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16122     GAME_CTRL_ID_PLAY,                          NULL,
16123     TRUE, FALSE,                                "play game"
16124   },
16125   {
16126     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16127     GAME_CTRL_ID_UNDO,                          NULL,
16128     TRUE, FALSE,                                "undo step"
16129   },
16130   {
16131     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16132     GAME_CTRL_ID_REDO,                          NULL,
16133     TRUE, FALSE,                                "redo step"
16134   },
16135   {
16136     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16137     GAME_CTRL_ID_SAVE,                          NULL,
16138     TRUE, FALSE,                                "save game"
16139   },
16140   {
16141     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16142     GAME_CTRL_ID_PAUSE2,                        NULL,
16143     TRUE, FALSE,                                "pause game"
16144   },
16145   {
16146     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16147     GAME_CTRL_ID_LOAD,                          NULL,
16148     TRUE, FALSE,                                "load game"
16149   },
16150   {
16151     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16152     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16153     FALSE, FALSE,                               "stop game"
16154   },
16155   {
16156     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16157     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16158     FALSE, FALSE,                               "pause game"
16159   },
16160   {
16161     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16162     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16163     FALSE, FALSE,                               "play game"
16164   },
16165   {
16166     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16167     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16168     FALSE, TRUE,                                "stop game"
16169   },
16170   {
16171     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16172     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16173     FALSE, TRUE,                                "pause game"
16174   },
16175   {
16176     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16177     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16178     TRUE, FALSE,                                "background music on/off"
16179   },
16180   {
16181     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16182     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16183     TRUE, FALSE,                                "sound loops on/off"
16184   },
16185   {
16186     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16187     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16188     TRUE, FALSE,                                "normal sounds on/off"
16189   },
16190   {
16191     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16192     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16193     FALSE, FALSE,                               "background music on/off"
16194   },
16195   {
16196     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16197     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16198     FALSE, FALSE,                               "sound loops on/off"
16199   },
16200   {
16201     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16202     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16203     FALSE, FALSE,                               "normal sounds on/off"
16204   }
16205 };
16206
16207 void CreateGameButtons(void)
16208 {
16209   int i;
16210
16211   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16212   {
16213     int graphic = gamebutton_info[i].graphic;
16214     struct GraphicInfo *gfx = &graphic_info[graphic];
16215     struct XY *pos = gamebutton_info[i].pos;
16216     struct GadgetInfo *gi;
16217     int button_type;
16218     boolean checked;
16219     unsigned int event_mask;
16220     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16221     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16222     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16223     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16224     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16225     int gd_x   = gfx->src_x;
16226     int gd_y   = gfx->src_y;
16227     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16228     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16229     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16230     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16231     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16232     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16233     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16234     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16235     int id = i;
16236
16237     if (gfx->bitmap == NULL)
16238     {
16239       game_gadget[id] = NULL;
16240
16241       continue;
16242     }
16243
16244     if (id == GAME_CTRL_ID_STOP ||
16245         id == GAME_CTRL_ID_PANEL_STOP ||
16246         id == GAME_CTRL_ID_TOUCH_STOP ||
16247         id == GAME_CTRL_ID_PLAY ||
16248         id == GAME_CTRL_ID_PANEL_PLAY ||
16249         id == GAME_CTRL_ID_SAVE ||
16250         id == GAME_CTRL_ID_LOAD)
16251     {
16252       button_type = GD_TYPE_NORMAL_BUTTON;
16253       checked = FALSE;
16254       event_mask = GD_EVENT_RELEASED;
16255     }
16256     else if (id == GAME_CTRL_ID_UNDO ||
16257              id == GAME_CTRL_ID_REDO)
16258     {
16259       button_type = GD_TYPE_NORMAL_BUTTON;
16260       checked = FALSE;
16261       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16262     }
16263     else
16264     {
16265       button_type = GD_TYPE_CHECK_BUTTON;
16266       checked = (gamebutton_info[i].setup_value != NULL ?
16267                  *gamebutton_info[i].setup_value : FALSE);
16268       event_mask = GD_EVENT_PRESSED;
16269     }
16270
16271     gi = CreateGadget(GDI_CUSTOM_ID, id,
16272                       GDI_IMAGE_ID, graphic,
16273                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16274                       GDI_X, base_x + x,
16275                       GDI_Y, base_y + y,
16276                       GDI_WIDTH, gfx->width,
16277                       GDI_HEIGHT, gfx->height,
16278                       GDI_TYPE, button_type,
16279                       GDI_STATE, GD_BUTTON_UNPRESSED,
16280                       GDI_CHECKED, checked,
16281                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16282                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16283                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16284                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16285                       GDI_DIRECT_DRAW, FALSE,
16286                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16287                       GDI_EVENT_MASK, event_mask,
16288                       GDI_CALLBACK_ACTION, HandleGameButtons,
16289                       GDI_END);
16290
16291     if (gi == NULL)
16292       Fail("cannot create gadget");
16293
16294     game_gadget[id] = gi;
16295   }
16296 }
16297
16298 void FreeGameButtons(void)
16299 {
16300   int i;
16301
16302   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16303     FreeGadget(game_gadget[i]);
16304 }
16305
16306 static void UnmapGameButtonsAtSamePosition(int id)
16307 {
16308   int i;
16309
16310   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16311     if (i != id &&
16312         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16313         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16314       UnmapGadget(game_gadget[i]);
16315 }
16316
16317 static void UnmapGameButtonsAtSamePosition_All(void)
16318 {
16319   if (setup.show_load_save_buttons)
16320   {
16321     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16322     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16323     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16324   }
16325   else if (setup.show_undo_redo_buttons)
16326   {
16327     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16328     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16329     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16330   }
16331   else
16332   {
16333     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16334     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16335     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16336
16337     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16338     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16339     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16340   }
16341 }
16342
16343 void MapLoadSaveButtons(void)
16344 {
16345   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16346   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16347
16348   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16349   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16350 }
16351
16352 void MapUndoRedoButtons(void)
16353 {
16354   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16355   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16356
16357   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16358   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16359 }
16360
16361 void ModifyPauseButtons(void)
16362 {
16363   static int ids[] =
16364   {
16365     GAME_CTRL_ID_PAUSE,
16366     GAME_CTRL_ID_PAUSE2,
16367     GAME_CTRL_ID_PANEL_PAUSE,
16368     GAME_CTRL_ID_TOUCH_PAUSE,
16369     -1
16370   };
16371   int i;
16372
16373   for (i = 0; ids[i] > -1; i++)
16374     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16375 }
16376
16377 static void MapGameButtonsExt(boolean on_tape)
16378 {
16379   int i;
16380
16381   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16382     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16383       MapGadget(game_gadget[i]);
16384
16385   UnmapGameButtonsAtSamePosition_All();
16386
16387   RedrawGameButtons();
16388 }
16389
16390 static void UnmapGameButtonsExt(boolean on_tape)
16391 {
16392   int i;
16393
16394   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16395     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16396       UnmapGadget(game_gadget[i]);
16397 }
16398
16399 static void RedrawGameButtonsExt(boolean on_tape)
16400 {
16401   int i;
16402
16403   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16404     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16405       RedrawGadget(game_gadget[i]);
16406 }
16407
16408 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16409 {
16410   if (gi == NULL)
16411     return;
16412
16413   gi->checked = state;
16414 }
16415
16416 static void RedrawSoundButtonGadget(int id)
16417 {
16418   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16419              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16420              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16421              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16422              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16423              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16424              id);
16425
16426   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16427   RedrawGadget(game_gadget[id2]);
16428 }
16429
16430 void MapGameButtons(void)
16431 {
16432   MapGameButtonsExt(FALSE);
16433 }
16434
16435 void UnmapGameButtons(void)
16436 {
16437   UnmapGameButtonsExt(FALSE);
16438 }
16439
16440 void RedrawGameButtons(void)
16441 {
16442   RedrawGameButtonsExt(FALSE);
16443 }
16444
16445 void MapGameButtonsOnTape(void)
16446 {
16447   MapGameButtonsExt(TRUE);
16448 }
16449
16450 void UnmapGameButtonsOnTape(void)
16451 {
16452   UnmapGameButtonsExt(TRUE);
16453 }
16454
16455 void RedrawGameButtonsOnTape(void)
16456 {
16457   RedrawGameButtonsExt(TRUE);
16458 }
16459
16460 static void GameUndoRedoExt(void)
16461 {
16462   ClearPlayerAction();
16463
16464   tape.pausing = TRUE;
16465
16466   RedrawPlayfield();
16467   UpdateAndDisplayGameControlValues();
16468
16469   DrawCompleteVideoDisplay();
16470   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16471   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16472   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16473
16474   ModifyPauseButtons();
16475
16476   BackToFront();
16477 }
16478
16479 static void GameUndo(int steps)
16480 {
16481   if (!CheckEngineSnapshotList())
16482     return;
16483
16484   int tape_property_bits = tape.property_bits;
16485
16486   LoadEngineSnapshot_Undo(steps);
16487
16488   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16489
16490   GameUndoRedoExt();
16491 }
16492
16493 static void GameRedo(int steps)
16494 {
16495   if (!CheckEngineSnapshotList())
16496     return;
16497
16498   int tape_property_bits = tape.property_bits;
16499
16500   LoadEngineSnapshot_Redo(steps);
16501
16502   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16503
16504   GameUndoRedoExt();
16505 }
16506
16507 static void HandleGameButtonsExt(int id, int button)
16508 {
16509   static boolean game_undo_executed = FALSE;
16510   int steps = BUTTON_STEPSIZE(button);
16511   boolean handle_game_buttons =
16512     (game_status == GAME_MODE_PLAYING ||
16513      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16514
16515   if (!handle_game_buttons)
16516     return;
16517
16518   switch (id)
16519   {
16520     case GAME_CTRL_ID_STOP:
16521     case GAME_CTRL_ID_PANEL_STOP:
16522     case GAME_CTRL_ID_TOUCH_STOP:
16523       if (game_status == GAME_MODE_MAIN)
16524         break;
16525
16526       if (tape.playing)
16527         TapeStop();
16528       else
16529         RequestQuitGame(FALSE);
16530
16531       break;
16532
16533     case GAME_CTRL_ID_PAUSE:
16534     case GAME_CTRL_ID_PAUSE2:
16535     case GAME_CTRL_ID_PANEL_PAUSE:
16536     case GAME_CTRL_ID_TOUCH_PAUSE:
16537       if (network.enabled && game_status == GAME_MODE_PLAYING)
16538       {
16539         if (tape.pausing)
16540           SendToServer_ContinuePlaying();
16541         else
16542           SendToServer_PausePlaying();
16543       }
16544       else
16545         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16546
16547       game_undo_executed = FALSE;
16548
16549       break;
16550
16551     case GAME_CTRL_ID_PLAY:
16552     case GAME_CTRL_ID_PANEL_PLAY:
16553       if (game_status == GAME_MODE_MAIN)
16554       {
16555         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16556       }
16557       else if (tape.pausing)
16558       {
16559         if (network.enabled)
16560           SendToServer_ContinuePlaying();
16561         else
16562           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16563       }
16564       break;
16565
16566     case GAME_CTRL_ID_UNDO:
16567       // Important: When using "save snapshot when collecting an item" mode,
16568       // load last (current) snapshot for first "undo" after pressing "pause"
16569       // (else the last-but-one snapshot would be loaded, because the snapshot
16570       // pointer already points to the last snapshot when pressing "pause",
16571       // which is fine for "every step/move" mode, but not for "every collect")
16572       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16573           !game_undo_executed)
16574         steps--;
16575
16576       game_undo_executed = TRUE;
16577
16578       GameUndo(steps);
16579       break;
16580
16581     case GAME_CTRL_ID_REDO:
16582       GameRedo(steps);
16583       break;
16584
16585     case GAME_CTRL_ID_SAVE:
16586       TapeQuickSave();
16587       break;
16588
16589     case GAME_CTRL_ID_LOAD:
16590       TapeQuickLoad();
16591       break;
16592
16593     case SOUND_CTRL_ID_MUSIC:
16594     case SOUND_CTRL_ID_PANEL_MUSIC:
16595       if (setup.sound_music)
16596       { 
16597         setup.sound_music = FALSE;
16598
16599         FadeMusic();
16600       }
16601       else if (audio.music_available)
16602       { 
16603         setup.sound = setup.sound_music = TRUE;
16604
16605         SetAudioMode(setup.sound);
16606
16607         if (game_status == GAME_MODE_PLAYING)
16608           PlayLevelMusic();
16609       }
16610
16611       RedrawSoundButtonGadget(id);
16612
16613       break;
16614
16615     case SOUND_CTRL_ID_LOOPS:
16616     case SOUND_CTRL_ID_PANEL_LOOPS:
16617       if (setup.sound_loops)
16618         setup.sound_loops = FALSE;
16619       else if (audio.loops_available)
16620       {
16621         setup.sound = setup.sound_loops = TRUE;
16622
16623         SetAudioMode(setup.sound);
16624       }
16625
16626       RedrawSoundButtonGadget(id);
16627
16628       break;
16629
16630     case SOUND_CTRL_ID_SIMPLE:
16631     case SOUND_CTRL_ID_PANEL_SIMPLE:
16632       if (setup.sound_simple)
16633         setup.sound_simple = FALSE;
16634       else if (audio.sound_available)
16635       {
16636         setup.sound = setup.sound_simple = TRUE;
16637
16638         SetAudioMode(setup.sound);
16639       }
16640
16641       RedrawSoundButtonGadget(id);
16642
16643       break;
16644
16645     default:
16646       break;
16647   }
16648 }
16649
16650 static void HandleGameButtons(struct GadgetInfo *gi)
16651 {
16652   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16653 }
16654
16655 void HandleSoundButtonKeys(Key key)
16656 {
16657   if (key == setup.shortcut.sound_simple)
16658     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16659   else if (key == setup.shortcut.sound_loops)
16660     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16661   else if (key == setup.shortcut.sound_music)
16662     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16663 }