added counting mouse clicks on mouse click CEs when using step counter
[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         if (element_info[element].use_gfx_element)
2037           game.use_masked_elements = TRUE;
2038       }
2039
2040       break;
2041   }
2042
2043   if (!init_game)
2044     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2045 }
2046
2047 static void InitField_WithBug1(int x, int y, boolean init_game)
2048 {
2049   InitField(x, y, init_game);
2050
2051   // not needed to call InitMovDir() -- already done by InitField()!
2052   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2053       CAN_MOVE(Tile[x][y]))
2054     InitMovDir(x, y);
2055 }
2056
2057 static void InitField_WithBug2(int x, int y, boolean init_game)
2058 {
2059   int old_element = Tile[x][y];
2060
2061   InitField(x, y, init_game);
2062
2063   // not needed to call InitMovDir() -- already done by InitField()!
2064   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2065       CAN_MOVE(old_element) &&
2066       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2067     InitMovDir(x, y);
2068
2069   /* this case is in fact a combination of not less than three bugs:
2070      first, it calls InitMovDir() for elements that can move, although this is
2071      already done by InitField(); then, it checks the element that was at this
2072      field _before_ the call to InitField() (which can change it); lastly, it
2073      was not called for "mole with direction" elements, which were treated as
2074      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2075   */
2076 }
2077
2078 static int get_key_element_from_nr(int key_nr)
2079 {
2080   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2081                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2082                           EL_EM_KEY_1 : EL_KEY_1);
2083
2084   return key_base_element + key_nr;
2085 }
2086
2087 static int get_next_dropped_element(struct PlayerInfo *player)
2088 {
2089   return (player->inventory_size > 0 ?
2090           player->inventory_element[player->inventory_size - 1] :
2091           player->inventory_infinite_element != EL_UNDEFINED ?
2092           player->inventory_infinite_element :
2093           player->dynabombs_left > 0 ?
2094           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2095           EL_UNDEFINED);
2096 }
2097
2098 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2099 {
2100   // pos >= 0: get element from bottom of the stack;
2101   // pos <  0: get element from top of the stack
2102
2103   if (pos < 0)
2104   {
2105     int min_inventory_size = -pos;
2106     int inventory_pos = player->inventory_size - min_inventory_size;
2107     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2108
2109     return (player->inventory_size >= min_inventory_size ?
2110             player->inventory_element[inventory_pos] :
2111             player->inventory_infinite_element != EL_UNDEFINED ?
2112             player->inventory_infinite_element :
2113             player->dynabombs_left >= min_dynabombs_left ?
2114             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2115             EL_UNDEFINED);
2116   }
2117   else
2118   {
2119     int min_dynabombs_left = pos + 1;
2120     int min_inventory_size = pos + 1 - player->dynabombs_left;
2121     int inventory_pos = pos - player->dynabombs_left;
2122
2123     return (player->inventory_infinite_element != EL_UNDEFINED ?
2124             player->inventory_infinite_element :
2125             player->dynabombs_left >= min_dynabombs_left ?
2126             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2127             player->inventory_size >= min_inventory_size ?
2128             player->inventory_element[inventory_pos] :
2129             EL_UNDEFINED);
2130   }
2131 }
2132
2133 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2134 {
2135   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2136   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2137   int compare_result;
2138
2139   if (gpo1->sort_priority != gpo2->sort_priority)
2140     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2141   else
2142     compare_result = gpo1->nr - gpo2->nr;
2143
2144   return compare_result;
2145 }
2146
2147 int getPlayerInventorySize(int player_nr)
2148 {
2149   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2150     return game_em.ply[player_nr]->dynamite;
2151   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2152     return game_sp.red_disk_count;
2153   else
2154     return stored_player[player_nr].inventory_size;
2155 }
2156
2157 static void InitGameControlValues(void)
2158 {
2159   int i;
2160
2161   for (i = 0; game_panel_controls[i].nr != -1; i++)
2162   {
2163     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2164     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2165     struct TextPosInfo *pos = gpc->pos;
2166     int nr = gpc->nr;
2167     int type = gpc->type;
2168
2169     if (nr != i)
2170     {
2171       Error("'game_panel_controls' structure corrupted at %d", i);
2172
2173       Fail("this should not happen -- please debug");
2174     }
2175
2176     // force update of game controls after initialization
2177     gpc->value = gpc->last_value = -1;
2178     gpc->frame = gpc->last_frame = -1;
2179     gpc->gfx_frame = -1;
2180
2181     // determine panel value width for later calculation of alignment
2182     if (type == TYPE_INTEGER || type == TYPE_STRING)
2183     {
2184       pos->width = pos->size * getFontWidth(pos->font);
2185       pos->height = getFontHeight(pos->font);
2186     }
2187     else if (type == TYPE_ELEMENT)
2188     {
2189       pos->width = pos->size;
2190       pos->height = pos->size;
2191     }
2192
2193     // fill structure for game panel draw order
2194     gpo->nr = gpc->nr;
2195     gpo->sort_priority = pos->sort_priority;
2196   }
2197
2198   // sort game panel controls according to sort_priority and control number
2199   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2200         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2201 }
2202
2203 static void UpdatePlayfieldElementCount(void)
2204 {
2205   boolean use_element_count = FALSE;
2206   int i, j, x, y;
2207
2208   // first check if it is needed at all to calculate playfield element count
2209   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2210     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2211       use_element_count = TRUE;
2212
2213   if (!use_element_count)
2214     return;
2215
2216   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2217     element_info[i].element_count = 0;
2218
2219   SCAN_PLAYFIELD(x, y)
2220   {
2221     element_info[Tile[x][y]].element_count++;
2222   }
2223
2224   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2225     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2226       if (IS_IN_GROUP(j, i))
2227         element_info[EL_GROUP_START + i].element_count +=
2228           element_info[j].element_count;
2229 }
2230
2231 static void UpdateGameControlValues(void)
2232 {
2233   int i, k;
2234   int time = (game.LevelSolved ?
2235               game.LevelSolved_CountingTime :
2236               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2237               game_em.lev->time :
2238               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2239               game_sp.time_played :
2240               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2241               game_mm.energy_left :
2242               game.no_time_limit ? TimePlayed : TimeLeft);
2243   int score = (game.LevelSolved ?
2244                game.LevelSolved_CountingScore :
2245                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246                game_em.lev->score :
2247                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248                game_sp.score :
2249                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2250                game_mm.score :
2251                game.score);
2252   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2253               game_em.lev->gems_needed :
2254               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2255               game_sp.infotrons_still_needed :
2256               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2257               game_mm.kettles_still_needed :
2258               game.gems_still_needed);
2259   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2260                      game_em.lev->gems_needed > 0 :
2261                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2262                      game_sp.infotrons_still_needed > 0 :
2263                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2264                      game_mm.kettles_still_needed > 0 ||
2265                      game_mm.lights_still_needed > 0 :
2266                      game.gems_still_needed > 0 ||
2267                      game.sokoban_fields_still_needed > 0 ||
2268                      game.sokoban_objects_still_needed > 0 ||
2269                      game.lights_still_needed > 0);
2270   int health = (game.LevelSolved ?
2271                 game.LevelSolved_CountingHealth :
2272                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2273                 MM_HEALTH(game_mm.laser_overload_value) :
2274                 game.health);
2275   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2276
2277   UpdatePlayfieldElementCount();
2278
2279   // update game panel control values
2280
2281   // used instead of "level_nr" (for network games)
2282   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2283   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2284
2285   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2286   for (i = 0; i < MAX_NUM_KEYS; i++)
2287     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2288   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2289   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2290
2291   if (game.centered_player_nr == -1)
2292   {
2293     for (i = 0; i < MAX_PLAYERS; i++)
2294     {
2295       // only one player in Supaplex game engine
2296       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2297         break;
2298
2299       for (k = 0; k < MAX_NUM_KEYS; k++)
2300       {
2301         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2302         {
2303           if (game_em.ply[i]->keys & (1 << k))
2304             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2305               get_key_element_from_nr(k);
2306         }
2307         else if (stored_player[i].key[k])
2308           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2309             get_key_element_from_nr(k);
2310       }
2311
2312       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2313         getPlayerInventorySize(i);
2314
2315       if (stored_player[i].num_white_keys > 0)
2316         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2317           EL_DC_KEY_WHITE;
2318
2319       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2320         stored_player[i].num_white_keys;
2321     }
2322   }
2323   else
2324   {
2325     int player_nr = game.centered_player_nr;
2326
2327     for (k = 0; k < MAX_NUM_KEYS; k++)
2328     {
2329       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2330       {
2331         if (game_em.ply[player_nr]->keys & (1 << k))
2332           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2333             get_key_element_from_nr(k);
2334       }
2335       else if (stored_player[player_nr].key[k])
2336         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2337           get_key_element_from_nr(k);
2338     }
2339
2340     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2341       getPlayerInventorySize(player_nr);
2342
2343     if (stored_player[player_nr].num_white_keys > 0)
2344       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2345
2346     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2347       stored_player[player_nr].num_white_keys;
2348   }
2349
2350   // re-arrange keys on game panel, if needed or if defined by style settings
2351   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2352   {
2353     int nr = GAME_PANEL_KEY_1 + i;
2354     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2355     struct TextPosInfo *pos = gpc->pos;
2356
2357     // skip check if key is not in the player's inventory
2358     if (gpc->value == EL_EMPTY)
2359       continue;
2360
2361     // check if keys should be arranged on panel from left to right
2362     if (pos->style == STYLE_LEFTMOST_POSITION)
2363     {
2364       // check previous key positions (left from current key)
2365       for (k = 0; k < i; k++)
2366       {
2367         int nr_new = GAME_PANEL_KEY_1 + k;
2368
2369         if (game_panel_controls[nr_new].value == EL_EMPTY)
2370         {
2371           game_panel_controls[nr_new].value = gpc->value;
2372           gpc->value = EL_EMPTY;
2373
2374           break;
2375         }
2376       }
2377     }
2378
2379     // check if "undefined" keys can be placed at some other position
2380     if (pos->x == -1 && pos->y == -1)
2381     {
2382       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2383
2384       // 1st try: display key at the same position as normal or EM keys
2385       if (game_panel_controls[nr_new].value == EL_EMPTY)
2386       {
2387         game_panel_controls[nr_new].value = gpc->value;
2388       }
2389       else
2390       {
2391         // 2nd try: display key at the next free position in the key panel
2392         for (k = 0; k < STD_NUM_KEYS; k++)
2393         {
2394           nr_new = GAME_PANEL_KEY_1 + k;
2395
2396           if (game_panel_controls[nr_new].value == EL_EMPTY)
2397           {
2398             game_panel_controls[nr_new].value = gpc->value;
2399
2400             break;
2401           }
2402         }
2403       }
2404     }
2405   }
2406
2407   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2408   {
2409     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2410       get_inventory_element_from_pos(local_player, i);
2411     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2412       get_inventory_element_from_pos(local_player, -i - 1);
2413   }
2414
2415   game_panel_controls[GAME_PANEL_SCORE].value = score;
2416   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2417
2418   game_panel_controls[GAME_PANEL_TIME].value = time;
2419
2420   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2421   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2422   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2423
2424   if (level.time == 0)
2425     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2426   else
2427     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2428
2429   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2430   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2431
2432   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2433
2434   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2435     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2436      EL_EMPTY);
2437   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2438     local_player->shield_normal_time_left;
2439   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2440     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2441      EL_EMPTY);
2442   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2443     local_player->shield_deadly_time_left;
2444
2445   game_panel_controls[GAME_PANEL_EXIT].value =
2446     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2447
2448   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2449     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2450   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2451     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2452      EL_EMC_MAGIC_BALL_SWITCH);
2453
2454   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2455     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2456   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2457     game.light_time_left;
2458
2459   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2460     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2461   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2462     game.timegate_time_left;
2463
2464   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2465     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2466
2467   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2468     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2469   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2470     game.lenses_time_left;
2471
2472   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2473     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2474   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2475     game.magnify_time_left;
2476
2477   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2478     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2479      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2480      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2481      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2482      EL_BALLOON_SWITCH_NONE);
2483
2484   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2485     local_player->dynabomb_count;
2486   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2487     local_player->dynabomb_size;
2488   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2489     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2490
2491   game_panel_controls[GAME_PANEL_PENGUINS].value =
2492     game.friends_still_needed;
2493
2494   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2495     game.sokoban_objects_still_needed;
2496   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2497     game.sokoban_fields_still_needed;
2498
2499   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2500     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2501
2502   for (i = 0; i < NUM_BELTS; i++)
2503   {
2504     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2505       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2506        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2507     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2508       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2509   }
2510
2511   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2512     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2513   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2514     game.magic_wall_time_left;
2515
2516   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2517     local_player->gravity;
2518
2519   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2520     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2521
2522   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2523     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2524       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2525        game.panel.element[i].id : EL_UNDEFINED);
2526
2527   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2528     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2529       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2530        element_info[game.panel.element_count[i].id].element_count : 0);
2531
2532   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2533     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2534       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2535        element_info[game.panel.ce_score[i].id].collect_score : 0);
2536
2537   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2538     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2539       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2540        element_info[game.panel.ce_score_element[i].id].collect_score :
2541        EL_UNDEFINED);
2542
2543   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2544   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2545   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2546
2547   // update game panel control frames
2548
2549   for (i = 0; game_panel_controls[i].nr != -1; i++)
2550   {
2551     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2552
2553     if (gpc->type == TYPE_ELEMENT)
2554     {
2555       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2556       {
2557         int last_anim_random_frame = gfx.anim_random_frame;
2558         int element = gpc->value;
2559         int graphic = el2panelimg(element);
2560         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2561                                sync_random_frame : INIT_GFX_RANDOM());
2562
2563         if (gpc->value != gpc->last_value)
2564         {
2565           gpc->gfx_frame = 0;
2566           gpc->gfx_random = init_gfx_random;
2567         }
2568         else
2569         {
2570           gpc->gfx_frame++;
2571
2572           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2573               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2574             gpc->gfx_random = init_gfx_random;
2575         }
2576
2577         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2578           gfx.anim_random_frame = gpc->gfx_random;
2579
2580         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2581           gpc->gfx_frame = element_info[element].collect_score;
2582
2583         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2584
2585         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2586           gfx.anim_random_frame = last_anim_random_frame;
2587       }
2588     }
2589     else if (gpc->type == TYPE_GRAPHIC)
2590     {
2591       if (gpc->graphic != IMG_UNDEFINED)
2592       {
2593         int last_anim_random_frame = gfx.anim_random_frame;
2594         int graphic = gpc->graphic;
2595         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2596                                sync_random_frame : INIT_GFX_RANDOM());
2597
2598         if (gpc->value != gpc->last_value)
2599         {
2600           gpc->gfx_frame = 0;
2601           gpc->gfx_random = init_gfx_random;
2602         }
2603         else
2604         {
2605           gpc->gfx_frame++;
2606
2607           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2608               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2609             gpc->gfx_random = init_gfx_random;
2610         }
2611
2612         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2613           gfx.anim_random_frame = gpc->gfx_random;
2614
2615         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2616
2617         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2618           gfx.anim_random_frame = last_anim_random_frame;
2619       }
2620     }
2621   }
2622 }
2623
2624 static void DisplayGameControlValues(void)
2625 {
2626   boolean redraw_panel = FALSE;
2627   int i;
2628
2629   for (i = 0; game_panel_controls[i].nr != -1; i++)
2630   {
2631     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2632
2633     if (PANEL_DEACTIVATED(gpc->pos))
2634       continue;
2635
2636     if (gpc->value == gpc->last_value &&
2637         gpc->frame == gpc->last_frame)
2638       continue;
2639
2640     redraw_panel = TRUE;
2641   }
2642
2643   if (!redraw_panel)
2644     return;
2645
2646   // copy default game door content to main double buffer
2647
2648   // !!! CHECK AGAIN !!!
2649   SetPanelBackground();
2650   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2651   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2652
2653   // redraw game control buttons
2654   RedrawGameButtons();
2655
2656   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2657
2658   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2659   {
2660     int nr = game_panel_order[i].nr;
2661     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2662     struct TextPosInfo *pos = gpc->pos;
2663     int type = gpc->type;
2664     int value = gpc->value;
2665     int frame = gpc->frame;
2666     int size = pos->size;
2667     int font = pos->font;
2668     boolean draw_masked = pos->draw_masked;
2669     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2670
2671     if (PANEL_DEACTIVATED(pos))
2672       continue;
2673
2674     if (pos->class == get_hash_from_key("extra_panel_items") &&
2675         !setup.prefer_extra_panel_items)
2676       continue;
2677
2678     gpc->last_value = value;
2679     gpc->last_frame = frame;
2680
2681     if (type == TYPE_INTEGER)
2682     {
2683       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2684           nr == GAME_PANEL_TIME)
2685       {
2686         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2687
2688         if (use_dynamic_size)           // use dynamic number of digits
2689         {
2690           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2691           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2692           int size2 = size1 + 1;
2693           int font1 = pos->font;
2694           int font2 = pos->font_alt;
2695
2696           size = (value < value_change ? size1 : size2);
2697           font = (value < value_change ? font1 : font2);
2698         }
2699       }
2700
2701       // correct text size if "digits" is zero or less
2702       if (size <= 0)
2703         size = strlen(int2str(value, size));
2704
2705       // dynamically correct text alignment
2706       pos->width = size * getFontWidth(font);
2707
2708       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2709                   int2str(value, size), font, mask_mode);
2710     }
2711     else if (type == TYPE_ELEMENT)
2712     {
2713       int element, graphic;
2714       Bitmap *src_bitmap;
2715       int src_x, src_y;
2716       int width, height;
2717       int dst_x = PANEL_XPOS(pos);
2718       int dst_y = PANEL_YPOS(pos);
2719
2720       if (value != EL_UNDEFINED && value != EL_EMPTY)
2721       {
2722         element = value;
2723         graphic = el2panelimg(value);
2724
2725 #if 0
2726         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2727               element, EL_NAME(element), size);
2728 #endif
2729
2730         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2731           size = TILESIZE;
2732
2733         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2734                               &src_x, &src_y);
2735
2736         width  = graphic_info[graphic].width  * size / TILESIZE;
2737         height = graphic_info[graphic].height * size / TILESIZE;
2738
2739         if (draw_masked)
2740           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2741                            dst_x, dst_y);
2742         else
2743           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2744                      dst_x, dst_y);
2745       }
2746     }
2747     else if (type == TYPE_GRAPHIC)
2748     {
2749       int graphic        = gpc->graphic;
2750       int graphic_active = gpc->graphic_active;
2751       Bitmap *src_bitmap;
2752       int src_x, src_y;
2753       int width, height;
2754       int dst_x = PANEL_XPOS(pos);
2755       int dst_y = PANEL_YPOS(pos);
2756       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2757                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2758
2759       if (graphic != IMG_UNDEFINED && !skip)
2760       {
2761         if (pos->style == STYLE_REVERSE)
2762           value = 100 - value;
2763
2764         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2765
2766         if (pos->direction & MV_HORIZONTAL)
2767         {
2768           width  = graphic_info[graphic_active].width * value / 100;
2769           height = graphic_info[graphic_active].height;
2770
2771           if (pos->direction == MV_LEFT)
2772           {
2773             src_x += graphic_info[graphic_active].width - width;
2774             dst_x += graphic_info[graphic_active].width - width;
2775           }
2776         }
2777         else
2778         {
2779           width  = graphic_info[graphic_active].width;
2780           height = graphic_info[graphic_active].height * value / 100;
2781
2782           if (pos->direction == MV_UP)
2783           {
2784             src_y += graphic_info[graphic_active].height - height;
2785             dst_y += graphic_info[graphic_active].height - height;
2786           }
2787         }
2788
2789         if (draw_masked)
2790           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2791                            dst_x, dst_y);
2792         else
2793           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2794                      dst_x, dst_y);
2795
2796         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2797
2798         if (pos->direction & MV_HORIZONTAL)
2799         {
2800           if (pos->direction == MV_RIGHT)
2801           {
2802             src_x += width;
2803             dst_x += width;
2804           }
2805           else
2806           {
2807             dst_x = PANEL_XPOS(pos);
2808           }
2809
2810           width = graphic_info[graphic].width - width;
2811         }
2812         else
2813         {
2814           if (pos->direction == MV_DOWN)
2815           {
2816             src_y += height;
2817             dst_y += height;
2818           }
2819           else
2820           {
2821             dst_y = PANEL_YPOS(pos);
2822           }
2823
2824           height = graphic_info[graphic].height - height;
2825         }
2826
2827         if (draw_masked)
2828           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2829                            dst_x, dst_y);
2830         else
2831           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2832                      dst_x, dst_y);
2833       }
2834     }
2835     else if (type == TYPE_STRING)
2836     {
2837       boolean active = (value != 0);
2838       char *state_normal = "off";
2839       char *state_active = "on";
2840       char *state = (active ? state_active : state_normal);
2841       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2842                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2843                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2844                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2845
2846       if (nr == GAME_PANEL_GRAVITY_STATE)
2847       {
2848         int font1 = pos->font;          // (used for normal state)
2849         int font2 = pos->font_alt;      // (used for active state)
2850
2851         font = (active ? font2 : font1);
2852       }
2853
2854       if (s != NULL)
2855       {
2856         char *s_cut;
2857
2858         if (size <= 0)
2859         {
2860           // don't truncate output if "chars" is zero or less
2861           size = strlen(s);
2862
2863           // dynamically correct text alignment
2864           pos->width = size * getFontWidth(font);
2865         }
2866
2867         s_cut = getStringCopyN(s, size);
2868
2869         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2870                     s_cut, font, mask_mode);
2871
2872         free(s_cut);
2873       }
2874     }
2875
2876     redraw_mask |= REDRAW_DOOR_1;
2877   }
2878
2879   SetGameStatus(GAME_MODE_PLAYING);
2880 }
2881
2882 void UpdateAndDisplayGameControlValues(void)
2883 {
2884   if (tape.deactivate_display)
2885     return;
2886
2887   UpdateGameControlValues();
2888   DisplayGameControlValues();
2889 }
2890
2891 void UpdateGameDoorValues(void)
2892 {
2893   UpdateGameControlValues();
2894 }
2895
2896 void DrawGameDoorValues(void)
2897 {
2898   DisplayGameControlValues();
2899 }
2900
2901
2902 // ============================================================================
2903 // InitGameEngine()
2904 // ----------------------------------------------------------------------------
2905 // initialize game engine due to level / tape version number
2906 // ============================================================================
2907
2908 static void InitGameEngine(void)
2909 {
2910   int i, j, k, l, x, y;
2911
2912   // set game engine from tape file when re-playing, else from level file
2913   game.engine_version = (tape.playing ? tape.engine_version :
2914                          level.game_version);
2915
2916   // set single or multi-player game mode (needed for re-playing tapes)
2917   game.team_mode = setup.team_mode;
2918
2919   if (tape.playing)
2920   {
2921     int num_players = 0;
2922
2923     for (i = 0; i < MAX_PLAYERS; i++)
2924       if (tape.player_participates[i])
2925         num_players++;
2926
2927     // multi-player tapes contain input data for more than one player
2928     game.team_mode = (num_players > 1);
2929   }
2930
2931 #if 0
2932   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2933         level.game_version);
2934   Debug("game:init:level", "          tape.file_version   == %06d",
2935         tape.file_version);
2936   Debug("game:init:level", "          tape.game_version   == %06d",
2937         tape.game_version);
2938   Debug("game:init:level", "          tape.engine_version == %06d",
2939         tape.engine_version);
2940   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2941         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2942 #endif
2943
2944   // --------------------------------------------------------------------------
2945   // set flags for bugs and changes according to active game engine version
2946   // --------------------------------------------------------------------------
2947
2948   /*
2949     Summary of bugfix:
2950     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2951
2952     Bug was introduced in version:
2953     2.0.1
2954
2955     Bug was fixed in version:
2956     4.2.0.0
2957
2958     Description:
2959     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2960     but the property "can fall" was missing, which caused some levels to be
2961     unsolvable. This was fixed in version 4.2.0.0.
2962
2963     Affected levels/tapes:
2964     An example for a tape that was fixed by this bugfix is tape 029 from the
2965     level set "rnd_sam_bateman".
2966     The wrong behaviour will still be used for all levels or tapes that were
2967     created/recorded with it. An example for this is tape 023 from the level
2968     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2969   */
2970
2971   boolean use_amoeba_dropping_cannot_fall_bug =
2972     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2973       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2974      (tape.playing &&
2975       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2976       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2977
2978   /*
2979     Summary of bugfix/change:
2980     Fixed move speed of elements entering or leaving magic wall.
2981
2982     Fixed/changed in version:
2983     2.0.1
2984
2985     Description:
2986     Before 2.0.1, move speed of elements entering or leaving magic wall was
2987     twice as fast as it is now.
2988     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2989
2990     Affected levels/tapes:
2991     The first condition is generally needed for all levels/tapes before version
2992     2.0.1, which might use the old behaviour before it was changed; known tapes
2993     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2994     The second condition is an exception from the above case and is needed for
2995     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2996     above, but before it was known that this change would break tapes like the
2997     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2998     although the engine version while recording maybe was before 2.0.1. There
2999     are a lot of tapes that are affected by this exception, like tape 006 from
3000     the level set "rnd_conor_mancone".
3001   */
3002
3003   boolean use_old_move_stepsize_for_magic_wall =
3004     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3005      !(tape.playing &&
3006        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3007        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3008
3009   /*
3010     Summary of bugfix/change:
3011     Fixed handling for custom elements that change when pushed by the player.
3012
3013     Fixed/changed in version:
3014     3.1.0
3015
3016     Description:
3017     Before 3.1.0, custom elements that "change when pushing" changed directly
3018     after the player started pushing them (until then handled in "DigField()").
3019     Since 3.1.0, these custom elements are not changed until the "pushing"
3020     move of the element is finished (now handled in "ContinueMoving()").
3021
3022     Affected levels/tapes:
3023     The first condition is generally needed for all levels/tapes before version
3024     3.1.0, which might use the old behaviour before it was changed; known tapes
3025     that are affected are some tapes from the level set "Walpurgis Gardens" by
3026     Jamie Cullen.
3027     The second condition is an exception from the above case and is needed for
3028     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3029     above (including some development versions of 3.1.0), but before it was
3030     known that this change would break tapes like the above and was fixed in
3031     3.1.1, so that the changed behaviour was active although the engine version
3032     while recording maybe was before 3.1.0. There is at least one tape that is
3033     affected by this exception, which is the tape for the one-level set "Bug
3034     Machine" by Juergen Bonhagen.
3035   */
3036
3037   game.use_change_when_pushing_bug =
3038     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3039      !(tape.playing &&
3040        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3041        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3042
3043   /*
3044     Summary of bugfix/change:
3045     Fixed handling for blocking the field the player leaves when moving.
3046
3047     Fixed/changed in version:
3048     3.1.1
3049
3050     Description:
3051     Before 3.1.1, when "block last field when moving" was enabled, the field
3052     the player is leaving when moving was blocked for the time of the move,
3053     and was directly unblocked afterwards. This resulted in the last field
3054     being blocked for exactly one less than the number of frames of one player
3055     move. Additionally, even when blocking was disabled, the last field was
3056     blocked for exactly one frame.
3057     Since 3.1.1, due to changes in player movement handling, the last field
3058     is not blocked at all when blocking is disabled. When blocking is enabled,
3059     the last field is blocked for exactly the number of frames of one player
3060     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3061     last field is blocked for exactly one more than the number of frames of
3062     one player move.
3063
3064     Affected levels/tapes:
3065     (!!! yet to be determined -- probably many !!!)
3066   */
3067
3068   game.use_block_last_field_bug =
3069     (game.engine_version < VERSION_IDENT(3,1,1,0));
3070
3071   /* various special flags and settings for native Emerald Mine game engine */
3072
3073   game_em.use_single_button =
3074     (game.engine_version > VERSION_IDENT(4,0,0,2));
3075
3076   game_em.use_snap_key_bug =
3077     (game.engine_version < VERSION_IDENT(4,0,1,0));
3078
3079   game_em.use_random_bug =
3080     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3081
3082   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3083
3084   game_em.use_old_explosions            = use_old_em_engine;
3085   game_em.use_old_android               = use_old_em_engine;
3086   game_em.use_old_push_elements         = use_old_em_engine;
3087   game_em.use_old_push_into_acid        = use_old_em_engine;
3088
3089   game_em.use_wrap_around               = !use_old_em_engine;
3090
3091   // --------------------------------------------------------------------------
3092
3093   // set maximal allowed number of custom element changes per game frame
3094   game.max_num_changes_per_frame = 1;
3095
3096   // default scan direction: scan playfield from top/left to bottom/right
3097   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3098
3099   // dynamically adjust element properties according to game engine version
3100   InitElementPropertiesEngine(game.engine_version);
3101
3102   // ---------- initialize special element properties -------------------------
3103
3104   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3105   if (use_amoeba_dropping_cannot_fall_bug)
3106     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3107
3108   // ---------- initialize player's initial move delay ------------------------
3109
3110   // dynamically adjust player properties according to level information
3111   for (i = 0; i < MAX_PLAYERS; i++)
3112     game.initial_move_delay_value[i] =
3113       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3114
3115   // dynamically adjust player properties according to game engine version
3116   for (i = 0; i < MAX_PLAYERS; i++)
3117     game.initial_move_delay[i] =
3118       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3119        game.initial_move_delay_value[i] : 0);
3120
3121   // ---------- initialize player's initial push delay ------------------------
3122
3123   // dynamically adjust player properties according to game engine version
3124   game.initial_push_delay_value =
3125     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3126
3127   // ---------- initialize changing elements ----------------------------------
3128
3129   // initialize changing elements information
3130   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3131   {
3132     struct ElementInfo *ei = &element_info[i];
3133
3134     // this pointer might have been changed in the level editor
3135     ei->change = &ei->change_page[0];
3136
3137     if (!IS_CUSTOM_ELEMENT(i))
3138     {
3139       ei->change->target_element = EL_EMPTY_SPACE;
3140       ei->change->delay_fixed = 0;
3141       ei->change->delay_random = 0;
3142       ei->change->delay_frames = 1;
3143     }
3144
3145     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3146     {
3147       ei->has_change_event[j] = FALSE;
3148
3149       ei->event_page_nr[j] = 0;
3150       ei->event_page[j] = &ei->change_page[0];
3151     }
3152   }
3153
3154   // add changing elements from pre-defined list
3155   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3156   {
3157     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3158     struct ElementInfo *ei = &element_info[ch_delay->element];
3159
3160     ei->change->target_element       = ch_delay->target_element;
3161     ei->change->delay_fixed          = ch_delay->change_delay;
3162
3163     ei->change->pre_change_function  = ch_delay->pre_change_function;
3164     ei->change->change_function      = ch_delay->change_function;
3165     ei->change->post_change_function = ch_delay->post_change_function;
3166
3167     ei->change->can_change = TRUE;
3168     ei->change->can_change_or_has_action = TRUE;
3169
3170     ei->has_change_event[CE_DELAY] = TRUE;
3171
3172     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3173     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3174   }
3175
3176   // ---------- initialize internal run-time variables ------------------------
3177
3178   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3179   {
3180     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3181
3182     for (j = 0; j < ei->num_change_pages; j++)
3183     {
3184       ei->change_page[j].can_change_or_has_action =
3185         (ei->change_page[j].can_change |
3186          ei->change_page[j].has_action);
3187     }
3188   }
3189
3190   // add change events from custom element configuration
3191   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3192   {
3193     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3194
3195     for (j = 0; j < ei->num_change_pages; j++)
3196     {
3197       if (!ei->change_page[j].can_change_or_has_action)
3198         continue;
3199
3200       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3201       {
3202         // only add event page for the first page found with this event
3203         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3204         {
3205           ei->has_change_event[k] = TRUE;
3206
3207           ei->event_page_nr[k] = j;
3208           ei->event_page[k] = &ei->change_page[j];
3209         }
3210       }
3211     }
3212   }
3213
3214   // ---------- initialize reference elements in change conditions ------------
3215
3216   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3217   {
3218     int element = EL_CUSTOM_START + i;
3219     struct ElementInfo *ei = &element_info[element];
3220
3221     for (j = 0; j < ei->num_change_pages; j++)
3222     {
3223       int trigger_element = ei->change_page[j].initial_trigger_element;
3224
3225       if (trigger_element >= EL_PREV_CE_8 &&
3226           trigger_element <= EL_NEXT_CE_8)
3227         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3228
3229       ei->change_page[j].trigger_element = trigger_element;
3230     }
3231   }
3232
3233   // ---------- initialize run-time trigger player and element ----------------
3234
3235   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3236   {
3237     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3238
3239     for (j = 0; j < ei->num_change_pages; j++)
3240     {
3241       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3242       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3243       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3244       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3245       ei->change_page[j].actual_trigger_ce_value = 0;
3246       ei->change_page[j].actual_trigger_ce_score = 0;
3247     }
3248   }
3249
3250   // ---------- initialize trigger events -------------------------------------
3251
3252   // initialize trigger events information
3253   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3254     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3255       trigger_events[i][j] = FALSE;
3256
3257   // add trigger events from element change event properties
3258   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3259   {
3260     struct ElementInfo *ei = &element_info[i];
3261
3262     for (j = 0; j < ei->num_change_pages; j++)
3263     {
3264       if (!ei->change_page[j].can_change_or_has_action)
3265         continue;
3266
3267       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3268       {
3269         int trigger_element = ei->change_page[j].trigger_element;
3270
3271         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3272         {
3273           if (ei->change_page[j].has_event[k])
3274           {
3275             if (IS_GROUP_ELEMENT(trigger_element))
3276             {
3277               struct ElementGroupInfo *group =
3278                 element_info[trigger_element].group;
3279
3280               for (l = 0; l < group->num_elements_resolved; l++)
3281                 trigger_events[group->element_resolved[l]][k] = TRUE;
3282             }
3283             else if (trigger_element == EL_ANY_ELEMENT)
3284               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3285                 trigger_events[l][k] = TRUE;
3286             else
3287               trigger_events[trigger_element][k] = TRUE;
3288           }
3289         }
3290       }
3291     }
3292   }
3293
3294   // ---------- initialize push delay -----------------------------------------
3295
3296   // initialize push delay values to default
3297   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3298   {
3299     if (!IS_CUSTOM_ELEMENT(i))
3300     {
3301       // set default push delay values (corrected since version 3.0.7-1)
3302       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3303       {
3304         element_info[i].push_delay_fixed = 2;
3305         element_info[i].push_delay_random = 8;
3306       }
3307       else
3308       {
3309         element_info[i].push_delay_fixed = 8;
3310         element_info[i].push_delay_random = 8;
3311       }
3312     }
3313   }
3314
3315   // set push delay value for certain elements from pre-defined list
3316   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3317   {
3318     int e = push_delay_list[i].element;
3319
3320     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3321     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3322   }
3323
3324   // set push delay value for Supaplex elements for newer engine versions
3325   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3326   {
3327     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3328     {
3329       if (IS_SP_ELEMENT(i))
3330       {
3331         // set SP push delay to just enough to push under a falling zonk
3332         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3333
3334         element_info[i].push_delay_fixed  = delay;
3335         element_info[i].push_delay_random = 0;
3336       }
3337     }
3338   }
3339
3340   // ---------- initialize move stepsize --------------------------------------
3341
3342   // initialize move stepsize values to default
3343   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3344     if (!IS_CUSTOM_ELEMENT(i))
3345       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3346
3347   // set move stepsize value for certain elements from pre-defined list
3348   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3349   {
3350     int e = move_stepsize_list[i].element;
3351
3352     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3353
3354     // set move stepsize value for certain elements for older engine versions
3355     if (use_old_move_stepsize_for_magic_wall)
3356     {
3357       if (e == EL_MAGIC_WALL_FILLING ||
3358           e == EL_MAGIC_WALL_EMPTYING ||
3359           e == EL_BD_MAGIC_WALL_FILLING ||
3360           e == EL_BD_MAGIC_WALL_EMPTYING)
3361         element_info[e].move_stepsize *= 2;
3362     }
3363   }
3364
3365   // ---------- initialize collect score --------------------------------------
3366
3367   // initialize collect score values for custom elements from initial value
3368   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3369     if (IS_CUSTOM_ELEMENT(i))
3370       element_info[i].collect_score = element_info[i].collect_score_initial;
3371
3372   // ---------- initialize collect count --------------------------------------
3373
3374   // initialize collect count values for non-custom elements
3375   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3376     if (!IS_CUSTOM_ELEMENT(i))
3377       element_info[i].collect_count_initial = 0;
3378
3379   // add collect count values for all elements from pre-defined list
3380   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3381     element_info[collect_count_list[i].element].collect_count_initial =
3382       collect_count_list[i].count;
3383
3384   // ---------- initialize access direction -----------------------------------
3385
3386   // initialize access direction values to default (access from every side)
3387   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3388     if (!IS_CUSTOM_ELEMENT(i))
3389       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3390
3391   // set access direction value for certain elements from pre-defined list
3392   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3393     element_info[access_direction_list[i].element].access_direction =
3394       access_direction_list[i].direction;
3395
3396   // ---------- initialize explosion content ----------------------------------
3397   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3398   {
3399     if (IS_CUSTOM_ELEMENT(i))
3400       continue;
3401
3402     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3403     {
3404       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3405
3406       element_info[i].content.e[x][y] =
3407         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3408          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3409          i == EL_PLAYER_3 ? EL_EMERALD :
3410          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3411          i == EL_MOLE ? EL_EMERALD_RED :
3412          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3413          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3414          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3415          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3416          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3417          i == EL_WALL_EMERALD ? EL_EMERALD :
3418          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3419          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3420          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3421          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3422          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3423          i == EL_WALL_PEARL ? EL_PEARL :
3424          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3425          EL_EMPTY);
3426     }
3427   }
3428
3429   // ---------- initialize recursion detection --------------------------------
3430   recursion_loop_depth = 0;
3431   recursion_loop_detected = FALSE;
3432   recursion_loop_element = EL_UNDEFINED;
3433
3434   // ---------- initialize graphics engine ------------------------------------
3435   game.scroll_delay_value =
3436     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3437      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3438      !setup.forced_scroll_delay           ? 0 :
3439      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3440   game.scroll_delay_value =
3441     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3442
3443   // ---------- initialize game engine snapshots ------------------------------
3444   for (i = 0; i < MAX_PLAYERS; i++)
3445     game.snapshot.last_action[i] = 0;
3446   game.snapshot.changed_action = FALSE;
3447   game.snapshot.collected_item = FALSE;
3448   game.snapshot.mode =
3449     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3450      SNAPSHOT_MODE_EVERY_STEP :
3451      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3452      SNAPSHOT_MODE_EVERY_MOVE :
3453      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3454      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3455   game.snapshot.save_snapshot = FALSE;
3456
3457   // ---------- initialize level time for Supaplex engine ---------------------
3458   // Supaplex levels with time limit currently unsupported -- should be added
3459   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3460     level.time = 0;
3461
3462   // ---------- initialize flags for handling game actions --------------------
3463
3464   // set flags for game actions to default values
3465   game.use_key_actions = TRUE;
3466   game.use_mouse_actions = FALSE;
3467
3468   // when using Mirror Magic game engine, handle mouse events only
3469   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3470   {
3471     game.use_key_actions = FALSE;
3472     game.use_mouse_actions = TRUE;
3473   }
3474
3475   // check for custom elements with mouse click events
3476   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3477   {
3478     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3479     {
3480       int element = EL_CUSTOM_START + i;
3481
3482       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3483           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3484           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3485           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3486         game.use_mouse_actions = TRUE;
3487     }
3488   }
3489 }
3490
3491 static int get_num_special_action(int element, int action_first,
3492                                   int action_last)
3493 {
3494   int num_special_action = 0;
3495   int i, j;
3496
3497   for (i = action_first; i <= action_last; i++)
3498   {
3499     boolean found = FALSE;
3500
3501     for (j = 0; j < NUM_DIRECTIONS; j++)
3502       if (el_act_dir2img(element, i, j) !=
3503           el_act_dir2img(element, ACTION_DEFAULT, j))
3504         found = TRUE;
3505
3506     if (found)
3507       num_special_action++;
3508     else
3509       break;
3510   }
3511
3512   return num_special_action;
3513 }
3514
3515
3516 // ============================================================================
3517 // InitGame()
3518 // ----------------------------------------------------------------------------
3519 // initialize and start new game
3520 // ============================================================================
3521
3522 #if DEBUG_INIT_PLAYER
3523 static void DebugPrintPlayerStatus(char *message)
3524 {
3525   int i;
3526
3527   if (!options.debug)
3528     return;
3529
3530   Debug("game:init:player", "%s:", message);
3531
3532   for (i = 0; i < MAX_PLAYERS; i++)
3533   {
3534     struct PlayerInfo *player = &stored_player[i];
3535
3536     Debug("game:init:player",
3537           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3538           i + 1,
3539           player->present,
3540           player->connected,
3541           player->connected_locally,
3542           player->connected_network,
3543           player->active,
3544           (local_player == player ? " (local player)" : ""));
3545   }
3546 }
3547 #endif
3548
3549 void InitGame(void)
3550 {
3551   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3552   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3553   int fade_mask = REDRAW_FIELD;
3554
3555   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3556   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3557   int initial_move_dir = MV_DOWN;
3558   int i, j, x, y;
3559
3560   // required here to update video display before fading (FIX THIS)
3561   DrawMaskedBorder(REDRAW_DOOR_2);
3562
3563   if (!game.restart_level)
3564     CloseDoor(DOOR_CLOSE_1);
3565
3566   SetGameStatus(GAME_MODE_PLAYING);
3567
3568   if (level_editor_test_game)
3569     FadeSkipNextFadeOut();
3570   else
3571     FadeSetEnterScreen();
3572
3573   if (CheckFadeAll())
3574     fade_mask = REDRAW_ALL;
3575
3576   FadeLevelSoundsAndMusic();
3577
3578   ExpireSoundLoops(TRUE);
3579
3580   FadeOut(fade_mask);
3581
3582   if (level_editor_test_game)
3583     FadeSkipNextFadeIn();
3584
3585   // needed if different viewport properties defined for playing
3586   ChangeViewportPropertiesIfNeeded();
3587
3588   ClearField();
3589
3590   DrawCompleteVideoDisplay();
3591
3592   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3593
3594   InitGameEngine();
3595   InitGameControlValues();
3596
3597   if (tape.recording)
3598   {
3599     // initialize tape actions from game when recording tape
3600     tape.use_key_actions   = game.use_key_actions;
3601     tape.use_mouse_actions = game.use_mouse_actions;
3602
3603     // initialize visible playfield size when recording tape (for team mode)
3604     tape.scr_fieldx = SCR_FIELDX;
3605     tape.scr_fieldy = SCR_FIELDY;
3606   }
3607
3608   // don't play tapes over network
3609   network_playing = (network.enabled && !tape.playing);
3610
3611   for (i = 0; i < MAX_PLAYERS; i++)
3612   {
3613     struct PlayerInfo *player = &stored_player[i];
3614
3615     player->index_nr = i;
3616     player->index_bit = (1 << i);
3617     player->element_nr = EL_PLAYER_1 + i;
3618
3619     player->present = FALSE;
3620     player->active = FALSE;
3621     player->mapped = FALSE;
3622
3623     player->killed = FALSE;
3624     player->reanimated = FALSE;
3625     player->buried = FALSE;
3626
3627     player->action = 0;
3628     player->effective_action = 0;
3629     player->programmed_action = 0;
3630     player->snap_action = 0;
3631
3632     player->mouse_action.lx = 0;
3633     player->mouse_action.ly = 0;
3634     player->mouse_action.button = 0;
3635     player->mouse_action.button_hint = 0;
3636
3637     player->effective_mouse_action.lx = 0;
3638     player->effective_mouse_action.ly = 0;
3639     player->effective_mouse_action.button = 0;
3640     player->effective_mouse_action.button_hint = 0;
3641
3642     for (j = 0; j < MAX_NUM_KEYS; j++)
3643       player->key[j] = FALSE;
3644
3645     player->num_white_keys = 0;
3646
3647     player->dynabomb_count = 0;
3648     player->dynabomb_size = 1;
3649     player->dynabombs_left = 0;
3650     player->dynabomb_xl = FALSE;
3651
3652     player->MovDir = initial_move_dir;
3653     player->MovPos = 0;
3654     player->GfxPos = 0;
3655     player->GfxDir = initial_move_dir;
3656     player->GfxAction = ACTION_DEFAULT;
3657     player->Frame = 0;
3658     player->StepFrame = 0;
3659
3660     player->initial_element = player->element_nr;
3661     player->artwork_element =
3662       (level.use_artwork_element[i] ? level.artwork_element[i] :
3663        player->element_nr);
3664     player->use_murphy = FALSE;
3665
3666     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3667     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3668
3669     player->gravity = level.initial_player_gravity[i];
3670
3671     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3672
3673     player->actual_frame_counter = 0;
3674
3675     player->step_counter = 0;
3676
3677     player->last_move_dir = initial_move_dir;
3678
3679     player->is_active = FALSE;
3680
3681     player->is_waiting = FALSE;
3682     player->is_moving = FALSE;
3683     player->is_auto_moving = FALSE;
3684     player->is_digging = FALSE;
3685     player->is_snapping = FALSE;
3686     player->is_collecting = FALSE;
3687     player->is_pushing = FALSE;
3688     player->is_switching = FALSE;
3689     player->is_dropping = FALSE;
3690     player->is_dropping_pressed = FALSE;
3691
3692     player->is_bored = FALSE;
3693     player->is_sleeping = FALSE;
3694
3695     player->was_waiting = TRUE;
3696     player->was_moving = FALSE;
3697     player->was_snapping = FALSE;
3698     player->was_dropping = FALSE;
3699
3700     player->force_dropping = FALSE;
3701
3702     player->frame_counter_bored = -1;
3703     player->frame_counter_sleeping = -1;
3704
3705     player->anim_delay_counter = 0;
3706     player->post_delay_counter = 0;
3707
3708     player->dir_waiting = initial_move_dir;
3709     player->action_waiting = ACTION_DEFAULT;
3710     player->last_action_waiting = ACTION_DEFAULT;
3711     player->special_action_bored = ACTION_DEFAULT;
3712     player->special_action_sleeping = ACTION_DEFAULT;
3713
3714     player->switch_x = -1;
3715     player->switch_y = -1;
3716
3717     player->drop_x = -1;
3718     player->drop_y = -1;
3719
3720     player->show_envelope = 0;
3721
3722     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3723
3724     player->push_delay       = -1;      // initialized when pushing starts
3725     player->push_delay_value = game.initial_push_delay_value;
3726
3727     player->drop_delay = 0;
3728     player->drop_pressed_delay = 0;
3729
3730     player->last_jx = -1;
3731     player->last_jy = -1;
3732     player->jx = -1;
3733     player->jy = -1;
3734
3735     player->shield_normal_time_left = 0;
3736     player->shield_deadly_time_left = 0;
3737
3738     player->last_removed_element = EL_UNDEFINED;
3739
3740     player->inventory_infinite_element = EL_UNDEFINED;
3741     player->inventory_size = 0;
3742
3743     if (level.use_initial_inventory[i])
3744     {
3745       for (j = 0; j < level.initial_inventory_size[i]; j++)
3746       {
3747         int element = level.initial_inventory_content[i][j];
3748         int collect_count = element_info[element].collect_count_initial;
3749         int k;
3750
3751         if (!IS_CUSTOM_ELEMENT(element))
3752           collect_count = 1;
3753
3754         if (collect_count == 0)
3755           player->inventory_infinite_element = element;
3756         else
3757           for (k = 0; k < collect_count; k++)
3758             if (player->inventory_size < MAX_INVENTORY_SIZE)
3759               player->inventory_element[player->inventory_size++] = element;
3760       }
3761     }
3762
3763     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3764     SnapField(player, 0, 0);
3765
3766     map_player_action[i] = i;
3767   }
3768
3769   network_player_action_received = FALSE;
3770
3771   // initial null action
3772   if (network_playing)
3773     SendToServer_MovePlayer(MV_NONE);
3774
3775   FrameCounter = 0;
3776   TimeFrames = 0;
3777   TimePlayed = 0;
3778   TimeLeft = level.time;
3779   TapeTime = 0;
3780
3781   ScreenMovDir = MV_NONE;
3782   ScreenMovPos = 0;
3783   ScreenGfxPos = 0;
3784
3785   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3786
3787   game.robot_wheel_x = -1;
3788   game.robot_wheel_y = -1;
3789
3790   game.exit_x = -1;
3791   game.exit_y = -1;
3792
3793   game.all_players_gone = FALSE;
3794
3795   game.LevelSolved = FALSE;
3796   game.GameOver = FALSE;
3797
3798   game.GamePlayed = !tape.playing;
3799
3800   game.LevelSolved_GameWon = FALSE;
3801   game.LevelSolved_GameEnd = FALSE;
3802   game.LevelSolved_SaveTape = FALSE;
3803   game.LevelSolved_SaveScore = FALSE;
3804
3805   game.LevelSolved_CountingTime = 0;
3806   game.LevelSolved_CountingScore = 0;
3807   game.LevelSolved_CountingHealth = 0;
3808
3809   game.panel.active = TRUE;
3810
3811   game.no_time_limit = (level.time == 0);
3812
3813   game.yamyam_content_nr = 0;
3814   game.robot_wheel_active = FALSE;
3815   game.magic_wall_active = FALSE;
3816   game.magic_wall_time_left = 0;
3817   game.light_time_left = 0;
3818   game.timegate_time_left = 0;
3819   game.switchgate_pos = 0;
3820   game.wind_direction = level.wind_direction_initial;
3821
3822   game.time_final = 0;
3823   game.score_time_final = 0;
3824
3825   game.score = 0;
3826   game.score_final = 0;
3827
3828   game.health = MAX_HEALTH;
3829   game.health_final = MAX_HEALTH;
3830
3831   game.gems_still_needed = level.gems_needed;
3832   game.sokoban_fields_still_needed = 0;
3833   game.sokoban_objects_still_needed = 0;
3834   game.lights_still_needed = 0;
3835   game.players_still_needed = 0;
3836   game.friends_still_needed = 0;
3837
3838   game.lenses_time_left = 0;
3839   game.magnify_time_left = 0;
3840
3841   game.ball_active = level.ball_active_initial;
3842   game.ball_content_nr = 0;
3843
3844   game.explosions_delayed = TRUE;
3845
3846   game.envelope_active = FALSE;
3847
3848   // special case: set custom artwork setting to initial value
3849   game.use_masked_elements = game.use_masked_elements_initial;
3850
3851   for (i = 0; i < NUM_BELTS; i++)
3852   {
3853     game.belt_dir[i] = MV_NONE;
3854     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3855   }
3856
3857   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3858     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3859
3860 #if DEBUG_INIT_PLAYER
3861   DebugPrintPlayerStatus("Player status at level initialization");
3862 #endif
3863
3864   SCAN_PLAYFIELD(x, y)
3865   {
3866     Tile[x][y] = Last[x][y] = level.field[x][y];
3867     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3868     ChangeDelay[x][y] = 0;
3869     ChangePage[x][y] = -1;
3870     CustomValue[x][y] = 0;              // initialized in InitField()
3871     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3872     AmoebaNr[x][y] = 0;
3873     WasJustMoving[x][y] = 0;
3874     WasJustFalling[x][y] = 0;
3875     CheckCollision[x][y] = 0;
3876     CheckImpact[x][y] = 0;
3877     Stop[x][y] = FALSE;
3878     Pushed[x][y] = FALSE;
3879
3880     ChangeCount[x][y] = 0;
3881     ChangeEvent[x][y] = -1;
3882
3883     ExplodePhase[x][y] = 0;
3884     ExplodeDelay[x][y] = 0;
3885     ExplodeField[x][y] = EX_TYPE_NONE;
3886
3887     RunnerVisit[x][y] = 0;
3888     PlayerVisit[x][y] = 0;
3889
3890     GfxFrame[x][y] = 0;
3891     GfxRandom[x][y] = INIT_GFX_RANDOM();
3892     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3893     GfxElement[x][y] = EL_UNDEFINED;
3894     GfxElementEmpty[x][y] = EL_EMPTY;
3895     GfxAction[x][y] = ACTION_DEFAULT;
3896     GfxDir[x][y] = MV_NONE;
3897     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3898   }
3899
3900   SCAN_PLAYFIELD(x, y)
3901   {
3902     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3903       emulate_bd = FALSE;
3904     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3905       emulate_sp = FALSE;
3906
3907     InitField(x, y, TRUE);
3908
3909     ResetGfxAnimation(x, y);
3910   }
3911
3912   InitBeltMovement();
3913
3914   for (i = 0; i < MAX_PLAYERS; i++)
3915   {
3916     struct PlayerInfo *player = &stored_player[i];
3917
3918     // set number of special actions for bored and sleeping animation
3919     player->num_special_action_bored =
3920       get_num_special_action(player->artwork_element,
3921                              ACTION_BORING_1, ACTION_BORING_LAST);
3922     player->num_special_action_sleeping =
3923       get_num_special_action(player->artwork_element,
3924                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3925   }
3926
3927   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3928                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3929
3930   // initialize type of slippery elements
3931   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3932   {
3933     if (!IS_CUSTOM_ELEMENT(i))
3934     {
3935       // default: elements slip down either to the left or right randomly
3936       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3937
3938       // SP style elements prefer to slip down on the left side
3939       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3940         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3941
3942       // BD style elements prefer to slip down on the left side
3943       if (game.emulation == EMU_BOULDERDASH)
3944         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3945     }
3946   }
3947
3948   // initialize explosion and ignition delay
3949   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3950   {
3951     if (!IS_CUSTOM_ELEMENT(i))
3952     {
3953       int num_phase = 8;
3954       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3955                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3956                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3957       int last_phase = (num_phase + 1) * delay;
3958       int half_phase = (num_phase / 2) * delay;
3959
3960       element_info[i].explosion_delay = last_phase - 1;
3961       element_info[i].ignition_delay = half_phase;
3962
3963       if (i == EL_BLACK_ORB)
3964         element_info[i].ignition_delay = 1;
3965     }
3966   }
3967
3968   // correct non-moving belts to start moving left
3969   for (i = 0; i < NUM_BELTS; i++)
3970     if (game.belt_dir[i] == MV_NONE)
3971       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3972
3973 #if USE_NEW_PLAYER_ASSIGNMENTS
3974   // use preferred player also in local single-player mode
3975   if (!network.enabled && !game.team_mode)
3976   {
3977     int new_index_nr = setup.network_player_nr;
3978
3979     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3980     {
3981       for (i = 0; i < MAX_PLAYERS; i++)
3982         stored_player[i].connected_locally = FALSE;
3983
3984       stored_player[new_index_nr].connected_locally = TRUE;
3985     }
3986   }
3987
3988   for (i = 0; i < MAX_PLAYERS; i++)
3989   {
3990     stored_player[i].connected = FALSE;
3991
3992     // in network game mode, the local player might not be the first player
3993     if (stored_player[i].connected_locally)
3994       local_player = &stored_player[i];
3995   }
3996
3997   if (!network.enabled)
3998     local_player->connected = TRUE;
3999
4000   if (tape.playing)
4001   {
4002     for (i = 0; i < MAX_PLAYERS; i++)
4003       stored_player[i].connected = tape.player_participates[i];
4004   }
4005   else if (network.enabled)
4006   {
4007     // add team mode players connected over the network (needed for correct
4008     // assignment of player figures from level to locally playing players)
4009
4010     for (i = 0; i < MAX_PLAYERS; i++)
4011       if (stored_player[i].connected_network)
4012         stored_player[i].connected = TRUE;
4013   }
4014   else if (game.team_mode)
4015   {
4016     // try to guess locally connected team mode players (needed for correct
4017     // assignment of player figures from level to locally playing players)
4018
4019     for (i = 0; i < MAX_PLAYERS; i++)
4020       if (setup.input[i].use_joystick ||
4021           setup.input[i].key.left != KSYM_UNDEFINED)
4022         stored_player[i].connected = TRUE;
4023   }
4024
4025 #if DEBUG_INIT_PLAYER
4026   DebugPrintPlayerStatus("Player status after level initialization");
4027 #endif
4028
4029 #if DEBUG_INIT_PLAYER
4030   Debug("game:init:player", "Reassigning players ...");
4031 #endif
4032
4033   // check if any connected player was not found in playfield
4034   for (i = 0; i < MAX_PLAYERS; i++)
4035   {
4036     struct PlayerInfo *player = &stored_player[i];
4037
4038     if (player->connected && !player->present)
4039     {
4040       struct PlayerInfo *field_player = NULL;
4041
4042 #if DEBUG_INIT_PLAYER
4043       Debug("game:init:player",
4044             "- looking for field player for player %d ...", i + 1);
4045 #endif
4046
4047       // assign first free player found that is present in the playfield
4048
4049       // first try: look for unmapped playfield player that is not connected
4050       for (j = 0; j < MAX_PLAYERS; j++)
4051         if (field_player == NULL &&
4052             stored_player[j].present &&
4053             !stored_player[j].mapped &&
4054             !stored_player[j].connected)
4055           field_player = &stored_player[j];
4056
4057       // second try: look for *any* unmapped playfield player
4058       for (j = 0; j < MAX_PLAYERS; j++)
4059         if (field_player == NULL &&
4060             stored_player[j].present &&
4061             !stored_player[j].mapped)
4062           field_player = &stored_player[j];
4063
4064       if (field_player != NULL)
4065       {
4066         int jx = field_player->jx, jy = field_player->jy;
4067
4068 #if DEBUG_INIT_PLAYER
4069         Debug("game:init:player", "- found player %d",
4070               field_player->index_nr + 1);
4071 #endif
4072
4073         player->present = FALSE;
4074         player->active = FALSE;
4075
4076         field_player->present = TRUE;
4077         field_player->active = TRUE;
4078
4079         /*
4080         player->initial_element = field_player->initial_element;
4081         player->artwork_element = field_player->artwork_element;
4082
4083         player->block_last_field       = field_player->block_last_field;
4084         player->block_delay_adjustment = field_player->block_delay_adjustment;
4085         */
4086
4087         StorePlayer[jx][jy] = field_player->element_nr;
4088
4089         field_player->jx = field_player->last_jx = jx;
4090         field_player->jy = field_player->last_jy = jy;
4091
4092         if (local_player == player)
4093           local_player = field_player;
4094
4095         map_player_action[field_player->index_nr] = i;
4096
4097         field_player->mapped = TRUE;
4098
4099 #if DEBUG_INIT_PLAYER
4100         Debug("game:init:player", "- map_player_action[%d] == %d",
4101               field_player->index_nr + 1, i + 1);
4102 #endif
4103       }
4104     }
4105
4106     if (player->connected && player->present)
4107       player->mapped = TRUE;
4108   }
4109
4110 #if DEBUG_INIT_PLAYER
4111   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4112 #endif
4113
4114 #else
4115
4116   // check if any connected player was not found in playfield
4117   for (i = 0; i < MAX_PLAYERS; i++)
4118   {
4119     struct PlayerInfo *player = &stored_player[i];
4120
4121     if (player->connected && !player->present)
4122     {
4123       for (j = 0; j < MAX_PLAYERS; j++)
4124       {
4125         struct PlayerInfo *field_player = &stored_player[j];
4126         int jx = field_player->jx, jy = field_player->jy;
4127
4128         // assign first free player found that is present in the playfield
4129         if (field_player->present && !field_player->connected)
4130         {
4131           player->present = TRUE;
4132           player->active = TRUE;
4133
4134           field_player->present = FALSE;
4135           field_player->active = FALSE;
4136
4137           player->initial_element = field_player->initial_element;
4138           player->artwork_element = field_player->artwork_element;
4139
4140           player->block_last_field       = field_player->block_last_field;
4141           player->block_delay_adjustment = field_player->block_delay_adjustment;
4142
4143           StorePlayer[jx][jy] = player->element_nr;
4144
4145           player->jx = player->last_jx = jx;
4146           player->jy = player->last_jy = jy;
4147
4148           break;
4149         }
4150       }
4151     }
4152   }
4153 #endif
4154
4155 #if 0
4156   Debug("game:init:player", "local_player->present == %d",
4157         local_player->present);
4158 #endif
4159
4160   // set focus to local player for network games, else to all players
4161   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4162   game.centered_player_nr_next = game.centered_player_nr;
4163   game.set_centered_player = FALSE;
4164   game.set_centered_player_wrap = FALSE;
4165
4166   if (network_playing && tape.recording)
4167   {
4168     // store client dependent player focus when recording network games
4169     tape.centered_player_nr_next = game.centered_player_nr_next;
4170     tape.set_centered_player = TRUE;
4171   }
4172
4173   if (tape.playing)
4174   {
4175     // when playing a tape, eliminate all players who do not participate
4176
4177 #if USE_NEW_PLAYER_ASSIGNMENTS
4178
4179     if (!game.team_mode)
4180     {
4181       for (i = 0; i < MAX_PLAYERS; i++)
4182       {
4183         if (stored_player[i].active &&
4184             !tape.player_participates[map_player_action[i]])
4185         {
4186           struct PlayerInfo *player = &stored_player[i];
4187           int jx = player->jx, jy = player->jy;
4188
4189 #if DEBUG_INIT_PLAYER
4190           Debug("game:init:player", "Removing player %d at (%d, %d)",
4191                 i + 1, jx, jy);
4192 #endif
4193
4194           player->active = FALSE;
4195           StorePlayer[jx][jy] = 0;
4196           Tile[jx][jy] = EL_EMPTY;
4197         }
4198       }
4199     }
4200
4201 #else
4202
4203     for (i = 0; i < MAX_PLAYERS; i++)
4204     {
4205       if (stored_player[i].active &&
4206           !tape.player_participates[i])
4207       {
4208         struct PlayerInfo *player = &stored_player[i];
4209         int jx = player->jx, jy = player->jy;
4210
4211         player->active = FALSE;
4212         StorePlayer[jx][jy] = 0;
4213         Tile[jx][jy] = EL_EMPTY;
4214       }
4215     }
4216 #endif
4217   }
4218   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4219   {
4220     // when in single player mode, eliminate all but the local player
4221
4222     for (i = 0; i < MAX_PLAYERS; i++)
4223     {
4224       struct PlayerInfo *player = &stored_player[i];
4225
4226       if (player->active && player != local_player)
4227       {
4228         int jx = player->jx, jy = player->jy;
4229
4230         player->active = FALSE;
4231         player->present = FALSE;
4232
4233         StorePlayer[jx][jy] = 0;
4234         Tile[jx][jy] = EL_EMPTY;
4235       }
4236     }
4237   }
4238
4239   for (i = 0; i < MAX_PLAYERS; i++)
4240     if (stored_player[i].active)
4241       game.players_still_needed++;
4242
4243   if (level.solved_by_one_player)
4244     game.players_still_needed = 1;
4245
4246   // when recording the game, store which players take part in the game
4247   if (tape.recording)
4248   {
4249 #if USE_NEW_PLAYER_ASSIGNMENTS
4250     for (i = 0; i < MAX_PLAYERS; i++)
4251       if (stored_player[i].connected)
4252         tape.player_participates[i] = TRUE;
4253 #else
4254     for (i = 0; i < MAX_PLAYERS; i++)
4255       if (stored_player[i].active)
4256         tape.player_participates[i] = TRUE;
4257 #endif
4258   }
4259
4260 #if DEBUG_INIT_PLAYER
4261   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4262 #endif
4263
4264   if (BorderElement == EL_EMPTY)
4265   {
4266     SBX_Left = 0;
4267     SBX_Right = lev_fieldx - SCR_FIELDX;
4268     SBY_Upper = 0;
4269     SBY_Lower = lev_fieldy - SCR_FIELDY;
4270   }
4271   else
4272   {
4273     SBX_Left = -1;
4274     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4275     SBY_Upper = -1;
4276     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4277   }
4278
4279   if (full_lev_fieldx <= SCR_FIELDX)
4280     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4281   if (full_lev_fieldy <= SCR_FIELDY)
4282     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4283
4284   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4285     SBX_Left--;
4286   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4287     SBY_Upper--;
4288
4289   // if local player not found, look for custom element that might create
4290   // the player (make some assumptions about the right custom element)
4291   if (!local_player->present)
4292   {
4293     int start_x = 0, start_y = 0;
4294     int found_rating = 0;
4295     int found_element = EL_UNDEFINED;
4296     int player_nr = local_player->index_nr;
4297
4298     SCAN_PLAYFIELD(x, y)
4299     {
4300       int element = Tile[x][y];
4301       int content;
4302       int xx, yy;
4303       boolean is_player;
4304
4305       if (level.use_start_element[player_nr] &&
4306           level.start_element[player_nr] == element &&
4307           found_rating < 4)
4308       {
4309         start_x = x;
4310         start_y = y;
4311
4312         found_rating = 4;
4313         found_element = element;
4314       }
4315
4316       if (!IS_CUSTOM_ELEMENT(element))
4317         continue;
4318
4319       if (CAN_CHANGE(element))
4320       {
4321         for (i = 0; i < element_info[element].num_change_pages; i++)
4322         {
4323           // check for player created from custom element as single target
4324           content = element_info[element].change_page[i].target_element;
4325           is_player = IS_PLAYER_ELEMENT(content);
4326
4327           if (is_player && (found_rating < 3 ||
4328                             (found_rating == 3 && element < found_element)))
4329           {
4330             start_x = x;
4331             start_y = y;
4332
4333             found_rating = 3;
4334             found_element = element;
4335           }
4336         }
4337       }
4338
4339       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4340       {
4341         // check for player created from custom element as explosion content
4342         content = element_info[element].content.e[xx][yy];
4343         is_player = IS_PLAYER_ELEMENT(content);
4344
4345         if (is_player && (found_rating < 2 ||
4346                           (found_rating == 2 && element < found_element)))
4347         {
4348           start_x = x + xx - 1;
4349           start_y = y + yy - 1;
4350
4351           found_rating = 2;
4352           found_element = element;
4353         }
4354
4355         if (!CAN_CHANGE(element))
4356           continue;
4357
4358         for (i = 0; i < element_info[element].num_change_pages; i++)
4359         {
4360           // check for player created from custom element as extended target
4361           content =
4362             element_info[element].change_page[i].target_content.e[xx][yy];
4363
4364           is_player = IS_PLAYER_ELEMENT(content);
4365
4366           if (is_player && (found_rating < 1 ||
4367                             (found_rating == 1 && element < found_element)))
4368           {
4369             start_x = x + xx - 1;
4370             start_y = y + yy - 1;
4371
4372             found_rating = 1;
4373             found_element = element;
4374           }
4375         }
4376       }
4377     }
4378
4379     scroll_x = SCROLL_POSITION_X(start_x);
4380     scroll_y = SCROLL_POSITION_Y(start_y);
4381   }
4382   else
4383   {
4384     scroll_x = SCROLL_POSITION_X(local_player->jx);
4385     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4386   }
4387
4388   // !!! FIX THIS (START) !!!
4389   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4390   {
4391     InitGameEngine_EM();
4392   }
4393   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4394   {
4395     InitGameEngine_SP();
4396   }
4397   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4398   {
4399     InitGameEngine_MM();
4400   }
4401   else
4402   {
4403     DrawLevel(REDRAW_FIELD);
4404     DrawAllPlayers();
4405
4406     // after drawing the level, correct some elements
4407     if (game.timegate_time_left == 0)
4408       CloseAllOpenTimegates();
4409   }
4410
4411   // blit playfield from scroll buffer to normal back buffer for fading in
4412   BlitScreenToBitmap(backbuffer);
4413   // !!! FIX THIS (END) !!!
4414
4415   DrawMaskedBorder(fade_mask);
4416
4417   FadeIn(fade_mask);
4418
4419 #if 1
4420   // full screen redraw is required at this point in the following cases:
4421   // - special editor door undrawn when game was started from level editor
4422   // - drawing area (playfield) was changed and has to be removed completely
4423   redraw_mask = REDRAW_ALL;
4424   BackToFront();
4425 #endif
4426
4427   if (!game.restart_level)
4428   {
4429     // copy default game door content to main double buffer
4430
4431     // !!! CHECK AGAIN !!!
4432     SetPanelBackground();
4433     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4434     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4435   }
4436
4437   SetPanelBackground();
4438   SetDrawBackgroundMask(REDRAW_DOOR_1);
4439
4440   UpdateAndDisplayGameControlValues();
4441
4442   if (!game.restart_level)
4443   {
4444     UnmapGameButtons();
4445     UnmapTapeButtons();
4446
4447     FreeGameButtons();
4448     CreateGameButtons();
4449
4450     MapGameButtons();
4451     MapTapeButtons();
4452
4453     // copy actual game door content to door double buffer for OpenDoor()
4454     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4455
4456     OpenDoor(DOOR_OPEN_ALL);
4457
4458     KeyboardAutoRepeatOffUnlessAutoplay();
4459
4460 #if DEBUG_INIT_PLAYER
4461     DebugPrintPlayerStatus("Player status (final)");
4462 #endif
4463   }
4464
4465   UnmapAllGadgets();
4466
4467   MapGameButtons();
4468   MapTapeButtons();
4469
4470   if (!game.restart_level && !tape.playing)
4471   {
4472     LevelStats_incPlayed(level_nr);
4473
4474     SaveLevelSetup_SeriesInfo();
4475   }
4476
4477   game.restart_level = FALSE;
4478   game.restart_game_message = NULL;
4479
4480   game.request_active = FALSE;
4481   game.request_active_or_moving = FALSE;
4482
4483   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4484     InitGameActions_MM();
4485
4486   SaveEngineSnapshotToListInitial();
4487
4488   if (!game.restart_level)
4489   {
4490     PlaySound(SND_GAME_STARTING);
4491
4492     if (setup.sound_music)
4493       PlayLevelMusic();
4494   }
4495
4496   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4497 }
4498
4499 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4500                         int actual_player_x, int actual_player_y)
4501 {
4502   // this is used for non-R'n'D game engines to update certain engine values
4503
4504   // needed to determine if sounds are played within the visible screen area
4505   scroll_x = actual_scroll_x;
4506   scroll_y = actual_scroll_y;
4507
4508   // needed to get player position for "follow finger" playing input method
4509   local_player->jx = actual_player_x;
4510   local_player->jy = actual_player_y;
4511 }
4512
4513 void InitMovDir(int x, int y)
4514 {
4515   int i, element = Tile[x][y];
4516   static int xy[4][2] =
4517   {
4518     {  0, +1 },
4519     { +1,  0 },
4520     {  0, -1 },
4521     { -1,  0 }
4522   };
4523   static int direction[3][4] =
4524   {
4525     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4526     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4527     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4528   };
4529
4530   switch (element)
4531   {
4532     case EL_BUG_RIGHT:
4533     case EL_BUG_UP:
4534     case EL_BUG_LEFT:
4535     case EL_BUG_DOWN:
4536       Tile[x][y] = EL_BUG;
4537       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4538       break;
4539
4540     case EL_SPACESHIP_RIGHT:
4541     case EL_SPACESHIP_UP:
4542     case EL_SPACESHIP_LEFT:
4543     case EL_SPACESHIP_DOWN:
4544       Tile[x][y] = EL_SPACESHIP;
4545       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4546       break;
4547
4548     case EL_BD_BUTTERFLY_RIGHT:
4549     case EL_BD_BUTTERFLY_UP:
4550     case EL_BD_BUTTERFLY_LEFT:
4551     case EL_BD_BUTTERFLY_DOWN:
4552       Tile[x][y] = EL_BD_BUTTERFLY;
4553       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4554       break;
4555
4556     case EL_BD_FIREFLY_RIGHT:
4557     case EL_BD_FIREFLY_UP:
4558     case EL_BD_FIREFLY_LEFT:
4559     case EL_BD_FIREFLY_DOWN:
4560       Tile[x][y] = EL_BD_FIREFLY;
4561       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4562       break;
4563
4564     case EL_PACMAN_RIGHT:
4565     case EL_PACMAN_UP:
4566     case EL_PACMAN_LEFT:
4567     case EL_PACMAN_DOWN:
4568       Tile[x][y] = EL_PACMAN;
4569       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4570       break;
4571
4572     case EL_YAMYAM_LEFT:
4573     case EL_YAMYAM_RIGHT:
4574     case EL_YAMYAM_UP:
4575     case EL_YAMYAM_DOWN:
4576       Tile[x][y] = EL_YAMYAM;
4577       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4578       break;
4579
4580     case EL_SP_SNIKSNAK:
4581       MovDir[x][y] = MV_UP;
4582       break;
4583
4584     case EL_SP_ELECTRON:
4585       MovDir[x][y] = MV_LEFT;
4586       break;
4587
4588     case EL_MOLE_LEFT:
4589     case EL_MOLE_RIGHT:
4590     case EL_MOLE_UP:
4591     case EL_MOLE_DOWN:
4592       Tile[x][y] = EL_MOLE;
4593       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4594       break;
4595
4596     case EL_SPRING_LEFT:
4597     case EL_SPRING_RIGHT:
4598       Tile[x][y] = EL_SPRING;
4599       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4600       break;
4601
4602     default:
4603       if (IS_CUSTOM_ELEMENT(element))
4604       {
4605         struct ElementInfo *ei = &element_info[element];
4606         int move_direction_initial = ei->move_direction_initial;
4607         int move_pattern = ei->move_pattern;
4608
4609         if (move_direction_initial == MV_START_PREVIOUS)
4610         {
4611           if (MovDir[x][y] != MV_NONE)
4612             return;
4613
4614           move_direction_initial = MV_START_AUTOMATIC;
4615         }
4616
4617         if (move_direction_initial == MV_START_RANDOM)
4618           MovDir[x][y] = 1 << RND(4);
4619         else if (move_direction_initial & MV_ANY_DIRECTION)
4620           MovDir[x][y] = move_direction_initial;
4621         else if (move_pattern == MV_ALL_DIRECTIONS ||
4622                  move_pattern == MV_TURNING_LEFT ||
4623                  move_pattern == MV_TURNING_RIGHT ||
4624                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4625                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4626                  move_pattern == MV_TURNING_RANDOM)
4627           MovDir[x][y] = 1 << RND(4);
4628         else if (move_pattern == MV_HORIZONTAL)
4629           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4630         else if (move_pattern == MV_VERTICAL)
4631           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4632         else if (move_pattern & MV_ANY_DIRECTION)
4633           MovDir[x][y] = element_info[element].move_pattern;
4634         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4635                  move_pattern == MV_ALONG_RIGHT_SIDE)
4636         {
4637           // use random direction as default start direction
4638           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4639             MovDir[x][y] = 1 << RND(4);
4640
4641           for (i = 0; i < NUM_DIRECTIONS; i++)
4642           {
4643             int x1 = x + xy[i][0];
4644             int y1 = y + xy[i][1];
4645
4646             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4647             {
4648               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4649                 MovDir[x][y] = direction[0][i];
4650               else
4651                 MovDir[x][y] = direction[1][i];
4652
4653               break;
4654             }
4655           }
4656         }                
4657       }
4658       else
4659       {
4660         MovDir[x][y] = 1 << RND(4);
4661
4662         if (element != EL_BUG &&
4663             element != EL_SPACESHIP &&
4664             element != EL_BD_BUTTERFLY &&
4665             element != EL_BD_FIREFLY)
4666           break;
4667
4668         for (i = 0; i < NUM_DIRECTIONS; i++)
4669         {
4670           int x1 = x + xy[i][0];
4671           int y1 = y + xy[i][1];
4672
4673           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4674           {
4675             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4676             {
4677               MovDir[x][y] = direction[0][i];
4678               break;
4679             }
4680             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4681                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4682             {
4683               MovDir[x][y] = direction[1][i];
4684               break;
4685             }
4686           }
4687         }
4688       }
4689       break;
4690   }
4691
4692   GfxDir[x][y] = MovDir[x][y];
4693 }
4694
4695 void InitAmoebaNr(int x, int y)
4696 {
4697   int i;
4698   int group_nr = AmoebaNeighbourNr(x, y);
4699
4700   if (group_nr == 0)
4701   {
4702     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4703     {
4704       if (AmoebaCnt[i] == 0)
4705       {
4706         group_nr = i;
4707         break;
4708       }
4709     }
4710   }
4711
4712   AmoebaNr[x][y] = group_nr;
4713   AmoebaCnt[group_nr]++;
4714   AmoebaCnt2[group_nr]++;
4715 }
4716
4717 static void LevelSolved_SetFinalGameValues(void)
4718 {
4719   game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4720   game.score_time_final = (level.use_step_counter ? TimePlayed :
4721                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4722
4723   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4724                       game_em.lev->score :
4725                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4726                       game_mm.score :
4727                       game.score);
4728
4729   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4730                        MM_HEALTH(game_mm.laser_overload_value) :
4731                        game.health);
4732
4733   game.LevelSolved_CountingTime = game.time_final;
4734   game.LevelSolved_CountingScore = game.score_final;
4735   game.LevelSolved_CountingHealth = game.health_final;
4736 }
4737
4738 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4739 {
4740   game.LevelSolved_CountingTime = time;
4741   game.LevelSolved_CountingScore = score;
4742   game.LevelSolved_CountingHealth = health;
4743
4744   game_panel_controls[GAME_PANEL_TIME].value = time;
4745   game_panel_controls[GAME_PANEL_SCORE].value = score;
4746   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4747
4748   DisplayGameControlValues();
4749 }
4750
4751 static void LevelSolved(void)
4752 {
4753   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4754       game.players_still_needed > 0)
4755     return;
4756
4757   game.LevelSolved = TRUE;
4758   game.GameOver = TRUE;
4759
4760   // needed here to display correct panel values while player walks into exit
4761   LevelSolved_SetFinalGameValues();
4762 }
4763
4764 void GameWon(void)
4765 {
4766   static int time_count_steps;
4767   static int time, time_final;
4768   static float score, score_final; // needed for time score < 10 for 10 seconds
4769   static int health, health_final;
4770   static int game_over_delay_1 = 0;
4771   static int game_over_delay_2 = 0;
4772   static int game_over_delay_3 = 0;
4773   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4774   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4775
4776   if (!game.LevelSolved_GameWon)
4777   {
4778     int i;
4779
4780     // do not start end game actions before the player stops moving (to exit)
4781     if (local_player->active && local_player->MovPos)
4782       return;
4783
4784     // calculate final game values after player finished walking into exit
4785     LevelSolved_SetFinalGameValues();
4786
4787     game.LevelSolved_GameWon = TRUE;
4788     game.LevelSolved_SaveTape = tape.recording;
4789     game.LevelSolved_SaveScore = !tape.playing;
4790
4791     if (!tape.playing)
4792     {
4793       LevelStats_incSolved(level_nr);
4794
4795       SaveLevelSetup_SeriesInfo();
4796     }
4797
4798     if (tape.auto_play)         // tape might already be stopped here
4799       tape.auto_play_level_solved = TRUE;
4800
4801     TapeStop();
4802
4803     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4804     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4805     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4806
4807     time = time_final = game.time_final;
4808     score = score_final = game.score_final;
4809     health = health_final = game.health_final;
4810
4811     // update game panel values before (delayed) counting of score (if any)
4812     LevelSolved_DisplayFinalGameValues(time, score, health);
4813
4814     // if level has time score defined, calculate new final game values
4815     if (time_score > 0)
4816     {
4817       int time_final_max = 999;
4818       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4819       int time_frames = 0;
4820       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4821       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4822
4823       if (TimeLeft > 0)
4824       {
4825         time_final = 0;
4826         time_frames = time_frames_left;
4827       }
4828       else if (game.no_time_limit && TimePlayed < time_final_max)
4829       {
4830         time_final = time_final_max;
4831         time_frames = time_frames_final_max - time_frames_played;
4832       }
4833
4834       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4835
4836       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4837
4838       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4839       {
4840         health_final = 0;
4841         score_final += health * time_score;
4842       }
4843
4844       game.score_final = score_final;
4845       game.health_final = health_final;
4846     }
4847
4848     // if not counting score after game, immediately update game panel values
4849     if (level_editor_test_game || !setup.count_score_after_game)
4850     {
4851       time = time_final;
4852       score = score_final;
4853
4854       LevelSolved_DisplayFinalGameValues(time, score, health);
4855     }
4856
4857     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4858     {
4859       // check if last player has left the level
4860       if (game.exit_x >= 0 &&
4861           game.exit_y >= 0)
4862       {
4863         int x = game.exit_x;
4864         int y = game.exit_y;
4865         int element = Tile[x][y];
4866
4867         // close exit door after last player
4868         if ((game.all_players_gone &&
4869              (element == EL_EXIT_OPEN ||
4870               element == EL_SP_EXIT_OPEN ||
4871               element == EL_STEEL_EXIT_OPEN)) ||
4872             element == EL_EM_EXIT_OPEN ||
4873             element == EL_EM_STEEL_EXIT_OPEN)
4874         {
4875
4876           Tile[x][y] =
4877             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4878              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4879              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4880              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4881              EL_EM_STEEL_EXIT_CLOSING);
4882
4883           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4884         }
4885
4886         // player disappears
4887         DrawLevelField(x, y);
4888       }
4889
4890       for (i = 0; i < MAX_PLAYERS; i++)
4891       {
4892         struct PlayerInfo *player = &stored_player[i];
4893
4894         if (player->present)
4895         {
4896           RemovePlayer(player);
4897
4898           // player disappears
4899           DrawLevelField(player->jx, player->jy);
4900         }
4901       }
4902     }
4903
4904     PlaySound(SND_GAME_WINNING);
4905   }
4906
4907   if (setup.count_score_after_game)
4908   {
4909     if (time != time_final)
4910     {
4911       if (game_over_delay_1 > 0)
4912       {
4913         game_over_delay_1--;
4914
4915         return;
4916       }
4917
4918       int time_to_go = ABS(time_final - time);
4919       int time_count_dir = (time < time_final ? +1 : -1);
4920
4921       if (time_to_go < time_count_steps)
4922         time_count_steps = 1;
4923
4924       time  += time_count_steps * time_count_dir;
4925       score += time_count_steps * time_score;
4926
4927       // set final score to correct rounding differences after counting score
4928       if (time == time_final)
4929         score = score_final;
4930
4931       LevelSolved_DisplayFinalGameValues(time, score, health);
4932
4933       if (time == time_final)
4934         StopSound(SND_GAME_LEVELTIME_BONUS);
4935       else if (setup.sound_loops)
4936         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4937       else
4938         PlaySound(SND_GAME_LEVELTIME_BONUS);
4939
4940       return;
4941     }
4942
4943     if (health != health_final)
4944     {
4945       if (game_over_delay_2 > 0)
4946       {
4947         game_over_delay_2--;
4948
4949         return;
4950       }
4951
4952       int health_count_dir = (health < health_final ? +1 : -1);
4953
4954       health += health_count_dir;
4955       score  += time_score;
4956
4957       LevelSolved_DisplayFinalGameValues(time, score, health);
4958
4959       if (health == health_final)
4960         StopSound(SND_GAME_LEVELTIME_BONUS);
4961       else if (setup.sound_loops)
4962         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4963       else
4964         PlaySound(SND_GAME_LEVELTIME_BONUS);
4965
4966       return;
4967     }
4968   }
4969
4970   game.panel.active = FALSE;
4971
4972   if (game_over_delay_3 > 0)
4973   {
4974     game_over_delay_3--;
4975
4976     return;
4977   }
4978
4979   GameEnd();
4980 }
4981
4982 void GameEnd(void)
4983 {
4984   // used instead of "level_nr" (needed for network games)
4985   int last_level_nr = levelset.level_nr;
4986   boolean tape_saved = FALSE;
4987
4988   game.LevelSolved_GameEnd = TRUE;
4989
4990   if (game.LevelSolved_SaveTape)
4991   {
4992     // make sure that request dialog to save tape does not open door again
4993     if (!global.use_envelope_request)
4994       CloseDoor(DOOR_CLOSE_1);
4995
4996     // ask to save tape
4997     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
4998
4999     // set unique basename for score tape (also saved in high score table)
5000     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5001   }
5002
5003   // if no tape is to be saved, close both doors simultaneously
5004   CloseDoor(DOOR_CLOSE_ALL);
5005
5006   if (level_editor_test_game)
5007   {
5008     SetGameStatus(GAME_MODE_MAIN);
5009
5010     DrawMainMenu();
5011
5012     return;
5013   }
5014
5015   if (!game.LevelSolved_SaveScore)
5016   {
5017     SetGameStatus(GAME_MODE_MAIN);
5018
5019     DrawMainMenu();
5020
5021     return;
5022   }
5023
5024   if (level_nr == leveldir_current->handicap_level)
5025   {
5026     leveldir_current->handicap_level++;
5027
5028     SaveLevelSetup_SeriesInfo();
5029   }
5030
5031   // save score and score tape before potentially erasing tape below
5032   NewHighScore(last_level_nr, tape_saved);
5033
5034   if (setup.increment_levels &&
5035       level_nr < leveldir_current->last_level &&
5036       !network_playing)
5037   {
5038     level_nr++;         // advance to next level
5039     TapeErase();        // start with empty tape
5040
5041     if (setup.auto_play_next_level)
5042     {
5043       LoadLevel(level_nr);
5044
5045       SaveLevelSetup_SeriesInfo();
5046     }
5047   }
5048
5049   if (scores.last_added >= 0 && setup.show_scores_after_game)
5050   {
5051     SetGameStatus(GAME_MODE_SCORES);
5052
5053     DrawHallOfFame(last_level_nr);
5054   }
5055   else if (setup.auto_play_next_level && setup.increment_levels &&
5056            last_level_nr < leveldir_current->last_level &&
5057            !network_playing)
5058   {
5059     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5060   }
5061   else
5062   {
5063     SetGameStatus(GAME_MODE_MAIN);
5064
5065     DrawMainMenu();
5066   }
5067 }
5068
5069 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5070                          boolean one_score_entry_per_name)
5071 {
5072   int i;
5073
5074   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5075     return -1;
5076
5077   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5078   {
5079     struct ScoreEntry *entry = &list->entry[i];
5080     boolean score_is_better = (new_entry->score >  entry->score);
5081     boolean score_is_equal  = (new_entry->score == entry->score);
5082     boolean time_is_better  = (new_entry->time  <  entry->time);
5083     boolean time_is_equal   = (new_entry->time  == entry->time);
5084     boolean better_by_score = (score_is_better ||
5085                                (score_is_equal && time_is_better));
5086     boolean better_by_time  = (time_is_better ||
5087                                (time_is_equal && score_is_better));
5088     boolean is_better = (level.rate_time_over_score ? better_by_time :
5089                          better_by_score);
5090     boolean entry_is_empty = (entry->score == 0 &&
5091                               entry->time == 0);
5092
5093     // prevent adding server score entries if also existing in local score file
5094     // (special case: historic score entries have an empty tape basename entry)
5095     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5096         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5097       return -1;
5098
5099     if (is_better || entry_is_empty)
5100     {
5101       // player has made it to the hall of fame
5102
5103       if (i < MAX_SCORE_ENTRIES - 1)
5104       {
5105         int m = MAX_SCORE_ENTRIES - 1;
5106         int l;
5107
5108         if (one_score_entry_per_name)
5109         {
5110           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5111             if (strEqual(list->entry[l].name, new_entry->name))
5112               m = l;
5113
5114           if (m == i)   // player's new highscore overwrites his old one
5115             goto put_into_list;
5116         }
5117
5118         for (l = m; l > i; l--)
5119           list->entry[l] = list->entry[l - 1];
5120       }
5121
5122       put_into_list:
5123
5124       *entry = *new_entry;
5125
5126       return i;
5127     }
5128     else if (one_score_entry_per_name &&
5129              strEqual(entry->name, new_entry->name))
5130     {
5131       // player already in high score list with better score or time
5132
5133       return -1;
5134     }
5135   }
5136
5137   return -1;
5138 }
5139
5140 void NewHighScore(int level_nr, boolean tape_saved)
5141 {
5142   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5143   boolean one_per_name = FALSE;
5144
5145   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5146   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5147
5148   new_entry.score = game.score_final;
5149   new_entry.time = game.score_time_final;
5150
5151   LoadScore(level_nr);
5152
5153   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5154
5155   if (scores.last_added < 0)
5156     return;
5157
5158   SaveScore(level_nr);
5159
5160   // store last added local score entry (before merging server scores)
5161   scores.last_added_local = scores.last_added;
5162
5163   if (!game.LevelSolved_SaveTape)
5164     return;
5165
5166   SaveScoreTape(level_nr);
5167
5168   if (setup.ask_for_using_api_server)
5169   {
5170     setup.use_api_server =
5171       Request("Upload your score and tape to the high score server?", REQ_ASK);
5172
5173     if (!setup.use_api_server)
5174       Request("Not using high score server! Use setup menu to enable again!",
5175               REQ_CONFIRM);
5176
5177     runtime.use_api_server = setup.use_api_server;
5178
5179     // after asking for using API server once, do not ask again
5180     setup.ask_for_using_api_server = FALSE;
5181
5182     SaveSetup_ServerSetup();
5183   }
5184
5185   SaveServerScore(level_nr, tape_saved);
5186 }
5187
5188 void MergeServerScore(void)
5189 {
5190   struct ScoreEntry last_added_entry;
5191   boolean one_per_name = FALSE;
5192   int i;
5193
5194   if (scores.last_added >= 0)
5195     last_added_entry = scores.entry[scores.last_added];
5196
5197   for (i = 0; i < server_scores.num_entries; i++)
5198   {
5199     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5200
5201     if (pos >= 0 && pos <= scores.last_added)
5202       scores.last_added++;
5203   }
5204
5205   if (scores.last_added >= MAX_SCORE_ENTRIES)
5206   {
5207     scores.last_added = MAX_SCORE_ENTRIES - 1;
5208     scores.force_last_added = TRUE;
5209
5210     scores.entry[scores.last_added] = last_added_entry;
5211   }
5212 }
5213
5214 static int getElementMoveStepsizeExt(int x, int y, int direction)
5215 {
5216   int element = Tile[x][y];
5217   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5218   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5219   int horiz_move = (dx != 0);
5220   int sign = (horiz_move ? dx : dy);
5221   int step = sign * element_info[element].move_stepsize;
5222
5223   // special values for move stepsize for spring and things on conveyor belt
5224   if (horiz_move)
5225   {
5226     if (CAN_FALL(element) &&
5227         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5228       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5229     else if (element == EL_SPRING)
5230       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5231   }
5232
5233   return step;
5234 }
5235
5236 static int getElementMoveStepsize(int x, int y)
5237 {
5238   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5239 }
5240
5241 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5242 {
5243   if (player->GfxAction != action || player->GfxDir != dir)
5244   {
5245     player->GfxAction = action;
5246     player->GfxDir = dir;
5247     player->Frame = 0;
5248     player->StepFrame = 0;
5249   }
5250 }
5251
5252 static void ResetGfxFrame(int x, int y)
5253 {
5254   // profiling showed that "autotest" spends 10~20% of its time in this function
5255   if (DrawingDeactivatedField())
5256     return;
5257
5258   int element = Tile[x][y];
5259   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5260
5261   if (graphic_info[graphic].anim_global_sync)
5262     GfxFrame[x][y] = FrameCounter;
5263   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5264     GfxFrame[x][y] = CustomValue[x][y];
5265   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5266     GfxFrame[x][y] = element_info[element].collect_score;
5267   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5268     GfxFrame[x][y] = ChangeDelay[x][y];
5269 }
5270
5271 static void ResetGfxAnimation(int x, int y)
5272 {
5273   GfxAction[x][y] = ACTION_DEFAULT;
5274   GfxDir[x][y] = MovDir[x][y];
5275   GfxFrame[x][y] = 0;
5276
5277   ResetGfxFrame(x, y);
5278 }
5279
5280 static void ResetRandomAnimationValue(int x, int y)
5281 {
5282   GfxRandom[x][y] = INIT_GFX_RANDOM();
5283 }
5284
5285 static void InitMovingField(int x, int y, int direction)
5286 {
5287   int element = Tile[x][y];
5288   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5289   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5290   int newx = x + dx;
5291   int newy = y + dy;
5292   boolean is_moving_before, is_moving_after;
5293
5294   // check if element was/is moving or being moved before/after mode change
5295   is_moving_before = (WasJustMoving[x][y] != 0);
5296   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5297
5298   // reset animation only for moving elements which change direction of moving
5299   // or which just started or stopped moving
5300   // (else CEs with property "can move" / "not moving" are reset each frame)
5301   if (is_moving_before != is_moving_after ||
5302       direction != MovDir[x][y])
5303     ResetGfxAnimation(x, y);
5304
5305   MovDir[x][y] = direction;
5306   GfxDir[x][y] = direction;
5307
5308   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5309                      direction == MV_DOWN && CAN_FALL(element) ?
5310                      ACTION_FALLING : ACTION_MOVING);
5311
5312   // this is needed for CEs with property "can move" / "not moving"
5313
5314   if (is_moving_after)
5315   {
5316     if (Tile[newx][newy] == EL_EMPTY)
5317       Tile[newx][newy] = EL_BLOCKED;
5318
5319     MovDir[newx][newy] = MovDir[x][y];
5320
5321     CustomValue[newx][newy] = CustomValue[x][y];
5322
5323     GfxFrame[newx][newy] = GfxFrame[x][y];
5324     GfxRandom[newx][newy] = GfxRandom[x][y];
5325     GfxAction[newx][newy] = GfxAction[x][y];
5326     GfxDir[newx][newy] = GfxDir[x][y];
5327   }
5328 }
5329
5330 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5331 {
5332   int direction = MovDir[x][y];
5333   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5334   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5335
5336   *goes_to_x = newx;
5337   *goes_to_y = newy;
5338 }
5339
5340 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5341 {
5342   int oldx = x, oldy = y;
5343   int direction = MovDir[x][y];
5344
5345   if (direction == MV_LEFT)
5346     oldx++;
5347   else if (direction == MV_RIGHT)
5348     oldx--;
5349   else if (direction == MV_UP)
5350     oldy++;
5351   else if (direction == MV_DOWN)
5352     oldy--;
5353
5354   *comes_from_x = oldx;
5355   *comes_from_y = oldy;
5356 }
5357
5358 static int MovingOrBlocked2Element(int x, int y)
5359 {
5360   int element = Tile[x][y];
5361
5362   if (element == EL_BLOCKED)
5363   {
5364     int oldx, oldy;
5365
5366     Blocked2Moving(x, y, &oldx, &oldy);
5367     return Tile[oldx][oldy];
5368   }
5369   else
5370     return element;
5371 }
5372
5373 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5374 {
5375   // like MovingOrBlocked2Element(), but if element is moving
5376   // and (x,y) is the field the moving element is just leaving,
5377   // return EL_BLOCKED instead of the element value
5378   int element = Tile[x][y];
5379
5380   if (IS_MOVING(x, y))
5381   {
5382     if (element == EL_BLOCKED)
5383     {
5384       int oldx, oldy;
5385
5386       Blocked2Moving(x, y, &oldx, &oldy);
5387       return Tile[oldx][oldy];
5388     }
5389     else
5390       return EL_BLOCKED;
5391   }
5392   else
5393     return element;
5394 }
5395
5396 static void RemoveField(int x, int y)
5397 {
5398   Tile[x][y] = EL_EMPTY;
5399
5400   MovPos[x][y] = 0;
5401   MovDir[x][y] = 0;
5402   MovDelay[x][y] = 0;
5403
5404   CustomValue[x][y] = 0;
5405
5406   AmoebaNr[x][y] = 0;
5407   ChangeDelay[x][y] = 0;
5408   ChangePage[x][y] = -1;
5409   Pushed[x][y] = FALSE;
5410
5411   GfxElement[x][y] = EL_UNDEFINED;
5412   GfxAction[x][y] = ACTION_DEFAULT;
5413   GfxDir[x][y] = MV_NONE;
5414 }
5415
5416 static void RemoveMovingField(int x, int y)
5417 {
5418   int oldx = x, oldy = y, newx = x, newy = y;
5419   int element = Tile[x][y];
5420   int next_element = EL_UNDEFINED;
5421
5422   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5423     return;
5424
5425   if (IS_MOVING(x, y))
5426   {
5427     Moving2Blocked(x, y, &newx, &newy);
5428
5429     if (Tile[newx][newy] != EL_BLOCKED)
5430     {
5431       // element is moving, but target field is not free (blocked), but
5432       // already occupied by something different (example: acid pool);
5433       // in this case, only remove the moving field, but not the target
5434
5435       RemoveField(oldx, oldy);
5436
5437       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5438
5439       TEST_DrawLevelField(oldx, oldy);
5440
5441       return;
5442     }
5443   }
5444   else if (element == EL_BLOCKED)
5445   {
5446     Blocked2Moving(x, y, &oldx, &oldy);
5447     if (!IS_MOVING(oldx, oldy))
5448       return;
5449   }
5450
5451   if (element == EL_BLOCKED &&
5452       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5453        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5454        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5455        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5456        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5457        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5458     next_element = get_next_element(Tile[oldx][oldy]);
5459
5460   RemoveField(oldx, oldy);
5461   RemoveField(newx, newy);
5462
5463   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5464
5465   if (next_element != EL_UNDEFINED)
5466     Tile[oldx][oldy] = next_element;
5467
5468   TEST_DrawLevelField(oldx, oldy);
5469   TEST_DrawLevelField(newx, newy);
5470 }
5471
5472 void DrawDynamite(int x, int y)
5473 {
5474   int sx = SCREENX(x), sy = SCREENY(y);
5475   int graphic = el2img(Tile[x][y]);
5476   int frame;
5477
5478   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5479     return;
5480
5481   if (IS_WALKABLE_INSIDE(Back[x][y]))
5482     return;
5483
5484   if (Back[x][y])
5485     DrawLevelElement(x, y, Back[x][y]);
5486   else if (Store[x][y])
5487     DrawLevelElement(x, y, Store[x][y]);
5488   else if (game.use_masked_elements)
5489     DrawLevelElement(x, y, EL_EMPTY);
5490
5491   frame = getGraphicAnimationFrameXY(graphic, x, y);
5492
5493   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5494     DrawGraphicThruMask(sx, sy, graphic, frame);
5495   else
5496     DrawGraphic(sx, sy, graphic, frame);
5497 }
5498
5499 static void CheckDynamite(int x, int y)
5500 {
5501   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5502   {
5503     MovDelay[x][y]--;
5504
5505     if (MovDelay[x][y] != 0)
5506     {
5507       DrawDynamite(x, y);
5508       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5509
5510       return;
5511     }
5512   }
5513
5514   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5515
5516   Bang(x, y);
5517 }
5518
5519 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5520 {
5521   boolean num_checked_players = 0;
5522   int i;
5523
5524   for (i = 0; i < MAX_PLAYERS; i++)
5525   {
5526     if (stored_player[i].active)
5527     {
5528       int sx = stored_player[i].jx;
5529       int sy = stored_player[i].jy;
5530
5531       if (num_checked_players == 0)
5532       {
5533         *sx1 = *sx2 = sx;
5534         *sy1 = *sy2 = sy;
5535       }
5536       else
5537       {
5538         *sx1 = MIN(*sx1, sx);
5539         *sy1 = MIN(*sy1, sy);
5540         *sx2 = MAX(*sx2, sx);
5541         *sy2 = MAX(*sy2, sy);
5542       }
5543
5544       num_checked_players++;
5545     }
5546   }
5547 }
5548
5549 static boolean checkIfAllPlayersFitToScreen_RND(void)
5550 {
5551   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5552
5553   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5554
5555   return (sx2 - sx1 < SCR_FIELDX &&
5556           sy2 - sy1 < SCR_FIELDY);
5557 }
5558
5559 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5560 {
5561   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5562
5563   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5564
5565   *sx = (sx1 + sx2) / 2;
5566   *sy = (sy1 + sy2) / 2;
5567 }
5568
5569 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5570                                boolean center_screen, boolean quick_relocation)
5571 {
5572   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5573   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5574   boolean no_delay = (tape.warp_forward);
5575   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5576   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5577   int new_scroll_x, new_scroll_y;
5578
5579   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5580   {
5581     // case 1: quick relocation inside visible screen (without scrolling)
5582
5583     RedrawPlayfield();
5584
5585     return;
5586   }
5587
5588   if (!level.shifted_relocation || center_screen)
5589   {
5590     // relocation _with_ centering of screen
5591
5592     new_scroll_x = SCROLL_POSITION_X(x);
5593     new_scroll_y = SCROLL_POSITION_Y(y);
5594   }
5595   else
5596   {
5597     // relocation _without_ centering of screen
5598
5599     int center_scroll_x = SCROLL_POSITION_X(old_x);
5600     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5601     int offset_x = x + (scroll_x - center_scroll_x);
5602     int offset_y = y + (scroll_y - center_scroll_y);
5603
5604     // for new screen position, apply previous offset to center position
5605     new_scroll_x = SCROLL_POSITION_X(offset_x);
5606     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5607   }
5608
5609   if (quick_relocation)
5610   {
5611     // case 2: quick relocation (redraw without visible scrolling)
5612
5613     scroll_x = new_scroll_x;
5614     scroll_y = new_scroll_y;
5615
5616     RedrawPlayfield();
5617
5618     return;
5619   }
5620
5621   // case 3: visible relocation (with scrolling to new position)
5622
5623   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5624
5625   SetVideoFrameDelay(wait_delay_value);
5626
5627   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5628   {
5629     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5630     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5631
5632     if (dx == 0 && dy == 0)             // no scrolling needed at all
5633       break;
5634
5635     scroll_x -= dx;
5636     scroll_y -= dy;
5637
5638     // set values for horizontal/vertical screen scrolling (half tile size)
5639     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5640     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5641     int pos_x = dx * TILEX / 2;
5642     int pos_y = dy * TILEY / 2;
5643     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5644     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5645
5646     ScrollLevel(dx, dy);
5647     DrawAllPlayers();
5648
5649     // scroll in two steps of half tile size to make things smoother
5650     BlitScreenToBitmapExt_RND(window, fx, fy);
5651
5652     // scroll second step to align at full tile size
5653     BlitScreenToBitmap(window);
5654   }
5655
5656   DrawAllPlayers();
5657   BackToFront();
5658
5659   SetVideoFrameDelay(frame_delay_value_old);
5660 }
5661
5662 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5663 {
5664   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5665   int player_nr = GET_PLAYER_NR(el_player);
5666   struct PlayerInfo *player = &stored_player[player_nr];
5667   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5668   boolean no_delay = (tape.warp_forward);
5669   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5670   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5671   int old_jx = player->jx;
5672   int old_jy = player->jy;
5673   int old_element = Tile[old_jx][old_jy];
5674   int element = Tile[jx][jy];
5675   boolean player_relocated = (old_jx != jx || old_jy != jy);
5676
5677   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5678   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5679   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5680   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5681   int leave_side_horiz = move_dir_horiz;
5682   int leave_side_vert  = move_dir_vert;
5683   int enter_side = enter_side_horiz | enter_side_vert;
5684   int leave_side = leave_side_horiz | leave_side_vert;
5685
5686   if (player->buried)           // do not reanimate dead player
5687     return;
5688
5689   if (!player_relocated)        // no need to relocate the player
5690     return;
5691
5692   if (IS_PLAYER(jx, jy))        // player already placed at new position
5693   {
5694     RemoveField(jx, jy);        // temporarily remove newly placed player
5695     DrawLevelField(jx, jy);
5696   }
5697
5698   if (player->present)
5699   {
5700     while (player->MovPos)
5701     {
5702       ScrollPlayer(player, SCROLL_GO_ON);
5703       ScrollScreen(NULL, SCROLL_GO_ON);
5704
5705       AdvanceFrameAndPlayerCounters(player->index_nr);
5706
5707       DrawPlayer(player);
5708
5709       BackToFront_WithFrameDelay(wait_delay_value);
5710     }
5711
5712     DrawPlayer(player);         // needed here only to cleanup last field
5713     DrawLevelField(player->jx, player->jy);     // remove player graphic
5714
5715     player->is_moving = FALSE;
5716   }
5717
5718   if (IS_CUSTOM_ELEMENT(old_element))
5719     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5720                                CE_LEFT_BY_PLAYER,
5721                                player->index_bit, leave_side);
5722
5723   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5724                                       CE_PLAYER_LEAVES_X,
5725                                       player->index_bit, leave_side);
5726
5727   Tile[jx][jy] = el_player;
5728   InitPlayerField(jx, jy, el_player, TRUE);
5729
5730   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5731      possible that the relocation target field did not contain a player element,
5732      but a walkable element, to which the new player was relocated -- in this
5733      case, restore that (already initialized!) element on the player field */
5734   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5735   {
5736     Tile[jx][jy] = element;     // restore previously existing element
5737   }
5738
5739   // only visually relocate centered player
5740   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5741                      FALSE, level.instant_relocation);
5742
5743   TestIfPlayerTouchesBadThing(jx, jy);
5744   TestIfPlayerTouchesCustomElement(jx, jy);
5745
5746   if (IS_CUSTOM_ELEMENT(element))
5747     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5748                                player->index_bit, enter_side);
5749
5750   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5751                                       player->index_bit, enter_side);
5752
5753   if (player->is_switching)
5754   {
5755     /* ensure that relocation while still switching an element does not cause
5756        a new element to be treated as also switched directly after relocation
5757        (this is important for teleporter switches that teleport the player to
5758        a place where another teleporter switch is in the same direction, which
5759        would then incorrectly be treated as immediately switched before the
5760        direction key that caused the switch was released) */
5761
5762     player->switch_x += jx - old_jx;
5763     player->switch_y += jy - old_jy;
5764   }
5765 }
5766
5767 static void Explode(int ex, int ey, int phase, int mode)
5768 {
5769   int x, y;
5770   int last_phase;
5771   int border_element;
5772
5773   // !!! eliminate this variable !!!
5774   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5775
5776   if (game.explosions_delayed)
5777   {
5778     ExplodeField[ex][ey] = mode;
5779     return;
5780   }
5781
5782   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5783   {
5784     int center_element = Tile[ex][ey];
5785     int artwork_element, explosion_element;     // set these values later
5786
5787     // remove things displayed in background while burning dynamite
5788     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5789       Back[ex][ey] = 0;
5790
5791     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5792     {
5793       // put moving element to center field (and let it explode there)
5794       center_element = MovingOrBlocked2Element(ex, ey);
5795       RemoveMovingField(ex, ey);
5796       Tile[ex][ey] = center_element;
5797     }
5798
5799     // now "center_element" is finally determined -- set related values now
5800     artwork_element = center_element;           // for custom player artwork
5801     explosion_element = center_element;         // for custom player artwork
5802
5803     if (IS_PLAYER(ex, ey))
5804     {
5805       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5806
5807       artwork_element = stored_player[player_nr].artwork_element;
5808
5809       if (level.use_explosion_element[player_nr])
5810       {
5811         explosion_element = level.explosion_element[player_nr];
5812         artwork_element = explosion_element;
5813       }
5814     }
5815
5816     if (mode == EX_TYPE_NORMAL ||
5817         mode == EX_TYPE_CENTER ||
5818         mode == EX_TYPE_CROSS)
5819       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5820
5821     last_phase = element_info[explosion_element].explosion_delay + 1;
5822
5823     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5824     {
5825       int xx = x - ex + 1;
5826       int yy = y - ey + 1;
5827       int element;
5828
5829       if (!IN_LEV_FIELD(x, y) ||
5830           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5831           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5832         continue;
5833
5834       element = Tile[x][y];
5835
5836       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5837       {
5838         element = MovingOrBlocked2Element(x, y);
5839
5840         if (!IS_EXPLOSION_PROOF(element))
5841           RemoveMovingField(x, y);
5842       }
5843
5844       // indestructible elements can only explode in center (but not flames)
5845       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5846                                            mode == EX_TYPE_BORDER)) ||
5847           element == EL_FLAMES)
5848         continue;
5849
5850       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5851          behaviour, for example when touching a yamyam that explodes to rocks
5852          with active deadly shield, a rock is created under the player !!! */
5853       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5854 #if 0
5855       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5856           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5857            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5858 #else
5859       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5860 #endif
5861       {
5862         if (IS_ACTIVE_BOMB(element))
5863         {
5864           // re-activate things under the bomb like gate or penguin
5865           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5866           Back[x][y] = 0;
5867         }
5868
5869         continue;
5870       }
5871
5872       // save walkable background elements while explosion on same tile
5873       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5874           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5875         Back[x][y] = element;
5876
5877       // ignite explodable elements reached by other explosion
5878       if (element == EL_EXPLOSION)
5879         element = Store2[x][y];
5880
5881       if (AmoebaNr[x][y] &&
5882           (element == EL_AMOEBA_FULL ||
5883            element == EL_BD_AMOEBA ||
5884            element == EL_AMOEBA_GROWING))
5885       {
5886         AmoebaCnt[AmoebaNr[x][y]]--;
5887         AmoebaCnt2[AmoebaNr[x][y]]--;
5888       }
5889
5890       RemoveField(x, y);
5891
5892       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5893       {
5894         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5895
5896         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5897
5898         if (PLAYERINFO(ex, ey)->use_murphy)
5899           Store[x][y] = EL_EMPTY;
5900       }
5901
5902       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5903       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5904       else if (IS_PLAYER_ELEMENT(center_element))
5905         Store[x][y] = EL_EMPTY;
5906       else if (center_element == EL_YAMYAM)
5907         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5908       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5909         Store[x][y] = element_info[center_element].content.e[xx][yy];
5910 #if 1
5911       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5912       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5913       // otherwise) -- FIX THIS !!!
5914       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5915         Store[x][y] = element_info[element].content.e[1][1];
5916 #else
5917       else if (!CAN_EXPLODE(element))
5918         Store[x][y] = element_info[element].content.e[1][1];
5919 #endif
5920       else
5921         Store[x][y] = EL_EMPTY;
5922
5923       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5924           center_element == EL_AMOEBA_TO_DIAMOND)
5925         Store2[x][y] = element;
5926
5927       Tile[x][y] = EL_EXPLOSION;
5928       GfxElement[x][y] = artwork_element;
5929
5930       ExplodePhase[x][y] = 1;
5931       ExplodeDelay[x][y] = last_phase;
5932
5933       Stop[x][y] = TRUE;
5934     }
5935
5936     if (center_element == EL_YAMYAM)
5937       game.yamyam_content_nr =
5938         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5939
5940     return;
5941   }
5942
5943   if (Stop[ex][ey])
5944     return;
5945
5946   x = ex;
5947   y = ey;
5948
5949   if (phase == 1)
5950     GfxFrame[x][y] = 0;         // restart explosion animation
5951
5952   last_phase = ExplodeDelay[x][y];
5953
5954   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5955
5956   // this can happen if the player leaves an explosion just in time
5957   if (GfxElement[x][y] == EL_UNDEFINED)
5958     GfxElement[x][y] = EL_EMPTY;
5959
5960   border_element = Store2[x][y];
5961   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5962     border_element = StorePlayer[x][y];
5963
5964   if (phase == element_info[border_element].ignition_delay ||
5965       phase == last_phase)
5966   {
5967     boolean border_explosion = FALSE;
5968
5969     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5970         !PLAYER_EXPLOSION_PROTECTED(x, y))
5971     {
5972       KillPlayerUnlessExplosionProtected(x, y);
5973       border_explosion = TRUE;
5974     }
5975     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5976     {
5977       Tile[x][y] = Store2[x][y];
5978       Store2[x][y] = 0;
5979       Bang(x, y);
5980       border_explosion = TRUE;
5981     }
5982     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5983     {
5984       AmoebaToDiamond(x, y);
5985       Store2[x][y] = 0;
5986       border_explosion = TRUE;
5987     }
5988
5989     // if an element just explodes due to another explosion (chain-reaction),
5990     // do not immediately end the new explosion when it was the last frame of
5991     // the explosion (as it would be done in the following "if"-statement!)
5992     if (border_explosion && phase == last_phase)
5993       return;
5994   }
5995
5996   if (phase == last_phase)
5997   {
5998     int element;
5999
6000     element = Tile[x][y] = Store[x][y];
6001     Store[x][y] = Store2[x][y] = 0;
6002     GfxElement[x][y] = EL_UNDEFINED;
6003
6004     // player can escape from explosions and might therefore be still alive
6005     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6006         element <= EL_PLAYER_IS_EXPLODING_4)
6007     {
6008       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6009       int explosion_element = EL_PLAYER_1 + player_nr;
6010       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6011       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6012
6013       if (level.use_explosion_element[player_nr])
6014         explosion_element = level.explosion_element[player_nr];
6015
6016       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6017                     element_info[explosion_element].content.e[xx][yy]);
6018     }
6019
6020     // restore probably existing indestructible background element
6021     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6022       element = Tile[x][y] = Back[x][y];
6023     Back[x][y] = 0;
6024
6025     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6026     GfxDir[x][y] = MV_NONE;
6027     ChangeDelay[x][y] = 0;
6028     ChangePage[x][y] = -1;
6029
6030     CustomValue[x][y] = 0;
6031
6032     InitField_WithBug2(x, y, FALSE);
6033
6034     TEST_DrawLevelField(x, y);
6035
6036     TestIfElementTouchesCustomElement(x, y);
6037
6038     if (GFX_CRUMBLED(element))
6039       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6040
6041     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6042       StorePlayer[x][y] = 0;
6043
6044     if (IS_PLAYER_ELEMENT(element))
6045       RelocatePlayer(x, y, element);
6046   }
6047   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6048   {
6049     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6050     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6051
6052     if (phase == delay)
6053       TEST_DrawLevelFieldCrumbled(x, y);
6054
6055     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6056     {
6057       DrawLevelElement(x, y, Back[x][y]);
6058       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6059     }
6060     else if (IS_WALKABLE_UNDER(Back[x][y]))
6061     {
6062       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6063       DrawLevelElementThruMask(x, y, Back[x][y]);
6064     }
6065     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6066       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6067   }
6068 }
6069
6070 static void DynaExplode(int ex, int ey)
6071 {
6072   int i, j;
6073   int dynabomb_element = Tile[ex][ey];
6074   int dynabomb_size = 1;
6075   boolean dynabomb_xl = FALSE;
6076   struct PlayerInfo *player;
6077   static int xy[4][2] =
6078   {
6079     { 0, -1 },
6080     { -1, 0 },
6081     { +1, 0 },
6082     { 0, +1 }
6083   };
6084
6085   if (IS_ACTIVE_BOMB(dynabomb_element))
6086   {
6087     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6088     dynabomb_size = player->dynabomb_size;
6089     dynabomb_xl = player->dynabomb_xl;
6090     player->dynabombs_left++;
6091   }
6092
6093   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6094
6095   for (i = 0; i < NUM_DIRECTIONS; i++)
6096   {
6097     for (j = 1; j <= dynabomb_size; j++)
6098     {
6099       int x = ex + j * xy[i][0];
6100       int y = ey + j * xy[i][1];
6101       int element;
6102
6103       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6104         break;
6105
6106       element = Tile[x][y];
6107
6108       // do not restart explosions of fields with active bombs
6109       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6110         continue;
6111
6112       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6113
6114       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6115           !IS_DIGGABLE(element) && !dynabomb_xl)
6116         break;
6117     }
6118   }
6119 }
6120
6121 void Bang(int x, int y)
6122 {
6123   int element = MovingOrBlocked2Element(x, y);
6124   int explosion_type = EX_TYPE_NORMAL;
6125
6126   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6127   {
6128     struct PlayerInfo *player = PLAYERINFO(x, y);
6129
6130     element = Tile[x][y] = player->initial_element;
6131
6132     if (level.use_explosion_element[player->index_nr])
6133     {
6134       int explosion_element = level.explosion_element[player->index_nr];
6135
6136       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6137         explosion_type = EX_TYPE_CROSS;
6138       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6139         explosion_type = EX_TYPE_CENTER;
6140     }
6141   }
6142
6143   switch (element)
6144   {
6145     case EL_BUG:
6146     case EL_SPACESHIP:
6147     case EL_BD_BUTTERFLY:
6148     case EL_BD_FIREFLY:
6149     case EL_YAMYAM:
6150     case EL_DARK_YAMYAM:
6151     case EL_ROBOT:
6152     case EL_PACMAN:
6153     case EL_MOLE:
6154       RaiseScoreElement(element);
6155       break;
6156
6157     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6158     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6159     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6160     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6161     case EL_DYNABOMB_INCREASE_NUMBER:
6162     case EL_DYNABOMB_INCREASE_SIZE:
6163     case EL_DYNABOMB_INCREASE_POWER:
6164       explosion_type = EX_TYPE_DYNA;
6165       break;
6166
6167     case EL_DC_LANDMINE:
6168       explosion_type = EX_TYPE_CENTER;
6169       break;
6170
6171     case EL_PENGUIN:
6172     case EL_LAMP:
6173     case EL_LAMP_ACTIVE:
6174     case EL_AMOEBA_TO_DIAMOND:
6175       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6176         explosion_type = EX_TYPE_CENTER;
6177       break;
6178
6179     default:
6180       if (element_info[element].explosion_type == EXPLODES_CROSS)
6181         explosion_type = EX_TYPE_CROSS;
6182       else if (element_info[element].explosion_type == EXPLODES_1X1)
6183         explosion_type = EX_TYPE_CENTER;
6184       break;
6185   }
6186
6187   if (explosion_type == EX_TYPE_DYNA)
6188     DynaExplode(x, y);
6189   else
6190     Explode(x, y, EX_PHASE_START, explosion_type);
6191
6192   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6193 }
6194
6195 static void SplashAcid(int x, int y)
6196 {
6197   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6198       (!IN_LEV_FIELD(x - 1, y - 2) ||
6199        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6200     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6201
6202   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6203       (!IN_LEV_FIELD(x + 1, y - 2) ||
6204        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6205     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6206
6207   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6208 }
6209
6210 static void InitBeltMovement(void)
6211 {
6212   static int belt_base_element[4] =
6213   {
6214     EL_CONVEYOR_BELT_1_LEFT,
6215     EL_CONVEYOR_BELT_2_LEFT,
6216     EL_CONVEYOR_BELT_3_LEFT,
6217     EL_CONVEYOR_BELT_4_LEFT
6218   };
6219   static int belt_base_active_element[4] =
6220   {
6221     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6222     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6223     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6224     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6225   };
6226
6227   int x, y, i, j;
6228
6229   // set frame order for belt animation graphic according to belt direction
6230   for (i = 0; i < NUM_BELTS; i++)
6231   {
6232     int belt_nr = i;
6233
6234     for (j = 0; j < NUM_BELT_PARTS; j++)
6235     {
6236       int element = belt_base_active_element[belt_nr] + j;
6237       int graphic_1 = el2img(element);
6238       int graphic_2 = el2panelimg(element);
6239
6240       if (game.belt_dir[i] == MV_LEFT)
6241       {
6242         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6243         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6244       }
6245       else
6246       {
6247         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6248         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6249       }
6250     }
6251   }
6252
6253   SCAN_PLAYFIELD(x, y)
6254   {
6255     int element = Tile[x][y];
6256
6257     for (i = 0; i < NUM_BELTS; i++)
6258     {
6259       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6260       {
6261         int e_belt_nr = getBeltNrFromBeltElement(element);
6262         int belt_nr = i;
6263
6264         if (e_belt_nr == belt_nr)
6265         {
6266           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6267
6268           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6269         }
6270       }
6271     }
6272   }
6273 }
6274
6275 static void ToggleBeltSwitch(int x, int y)
6276 {
6277   static int belt_base_element[4] =
6278   {
6279     EL_CONVEYOR_BELT_1_LEFT,
6280     EL_CONVEYOR_BELT_2_LEFT,
6281     EL_CONVEYOR_BELT_3_LEFT,
6282     EL_CONVEYOR_BELT_4_LEFT
6283   };
6284   static int belt_base_active_element[4] =
6285   {
6286     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6287     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6288     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6289     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6290   };
6291   static int belt_base_switch_element[4] =
6292   {
6293     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6294     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6295     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6296     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6297   };
6298   static int belt_move_dir[4] =
6299   {
6300     MV_LEFT,
6301     MV_NONE,
6302     MV_RIGHT,
6303     MV_NONE,
6304   };
6305
6306   int element = Tile[x][y];
6307   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6308   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6309   int belt_dir = belt_move_dir[belt_dir_nr];
6310   int xx, yy, i;
6311
6312   if (!IS_BELT_SWITCH(element))
6313     return;
6314
6315   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6316   game.belt_dir[belt_nr] = belt_dir;
6317
6318   if (belt_dir_nr == 3)
6319     belt_dir_nr = 1;
6320
6321   // set frame order for belt animation graphic according to belt direction
6322   for (i = 0; i < NUM_BELT_PARTS; i++)
6323   {
6324     int element = belt_base_active_element[belt_nr] + i;
6325     int graphic_1 = el2img(element);
6326     int graphic_2 = el2panelimg(element);
6327
6328     if (belt_dir == MV_LEFT)
6329     {
6330       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6331       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6332     }
6333     else
6334     {
6335       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6336       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6337     }
6338   }
6339
6340   SCAN_PLAYFIELD(xx, yy)
6341   {
6342     int element = Tile[xx][yy];
6343
6344     if (IS_BELT_SWITCH(element))
6345     {
6346       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6347
6348       if (e_belt_nr == belt_nr)
6349       {
6350         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6351         TEST_DrawLevelField(xx, yy);
6352       }
6353     }
6354     else if (IS_BELT(element) && belt_dir != MV_NONE)
6355     {
6356       int e_belt_nr = getBeltNrFromBeltElement(element);
6357
6358       if (e_belt_nr == belt_nr)
6359       {
6360         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6361
6362         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6363         TEST_DrawLevelField(xx, yy);
6364       }
6365     }
6366     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6367     {
6368       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6369
6370       if (e_belt_nr == belt_nr)
6371       {
6372         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6373
6374         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6375         TEST_DrawLevelField(xx, yy);
6376       }
6377     }
6378   }
6379 }
6380
6381 static void ToggleSwitchgateSwitch(int x, int y)
6382 {
6383   int xx, yy;
6384
6385   game.switchgate_pos = !game.switchgate_pos;
6386
6387   SCAN_PLAYFIELD(xx, yy)
6388   {
6389     int element = Tile[xx][yy];
6390
6391     if (element == EL_SWITCHGATE_SWITCH_UP)
6392     {
6393       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6394       TEST_DrawLevelField(xx, yy);
6395     }
6396     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6397     {
6398       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6399       TEST_DrawLevelField(xx, yy);
6400     }
6401     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6402     {
6403       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6404       TEST_DrawLevelField(xx, yy);
6405     }
6406     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6407     {
6408       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6409       TEST_DrawLevelField(xx, yy);
6410     }
6411     else if (element == EL_SWITCHGATE_OPEN ||
6412              element == EL_SWITCHGATE_OPENING)
6413     {
6414       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6415
6416       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6417     }
6418     else if (element == EL_SWITCHGATE_CLOSED ||
6419              element == EL_SWITCHGATE_CLOSING)
6420     {
6421       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6422
6423       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6424     }
6425   }
6426 }
6427
6428 static int getInvisibleActiveFromInvisibleElement(int element)
6429 {
6430   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6431           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6432           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6433           element);
6434 }
6435
6436 static int getInvisibleFromInvisibleActiveElement(int element)
6437 {
6438   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6439           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6440           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6441           element);
6442 }
6443
6444 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6445 {
6446   int x, y;
6447
6448   SCAN_PLAYFIELD(x, y)
6449   {
6450     int element = Tile[x][y];
6451
6452     if (element == EL_LIGHT_SWITCH &&
6453         game.light_time_left > 0)
6454     {
6455       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6456       TEST_DrawLevelField(x, y);
6457     }
6458     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6459              game.light_time_left == 0)
6460     {
6461       Tile[x][y] = EL_LIGHT_SWITCH;
6462       TEST_DrawLevelField(x, y);
6463     }
6464     else if (element == EL_EMC_DRIPPER &&
6465              game.light_time_left > 0)
6466     {
6467       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6468       TEST_DrawLevelField(x, y);
6469     }
6470     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6471              game.light_time_left == 0)
6472     {
6473       Tile[x][y] = EL_EMC_DRIPPER;
6474       TEST_DrawLevelField(x, y);
6475     }
6476     else if (element == EL_INVISIBLE_STEELWALL ||
6477              element == EL_INVISIBLE_WALL ||
6478              element == EL_INVISIBLE_SAND)
6479     {
6480       if (game.light_time_left > 0)
6481         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6482
6483       TEST_DrawLevelField(x, y);
6484
6485       // uncrumble neighbour fields, if needed
6486       if (element == EL_INVISIBLE_SAND)
6487         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6488     }
6489     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6490              element == EL_INVISIBLE_WALL_ACTIVE ||
6491              element == EL_INVISIBLE_SAND_ACTIVE)
6492     {
6493       if (game.light_time_left == 0)
6494         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6495
6496       TEST_DrawLevelField(x, y);
6497
6498       // re-crumble neighbour fields, if needed
6499       if (element == EL_INVISIBLE_SAND)
6500         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6501     }
6502   }
6503 }
6504
6505 static void RedrawAllInvisibleElementsForLenses(void)
6506 {
6507   int x, y;
6508
6509   SCAN_PLAYFIELD(x, y)
6510   {
6511     int element = Tile[x][y];
6512
6513     if (element == EL_EMC_DRIPPER &&
6514         game.lenses_time_left > 0)
6515     {
6516       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6517       TEST_DrawLevelField(x, y);
6518     }
6519     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6520              game.lenses_time_left == 0)
6521     {
6522       Tile[x][y] = EL_EMC_DRIPPER;
6523       TEST_DrawLevelField(x, y);
6524     }
6525     else if (element == EL_INVISIBLE_STEELWALL ||
6526              element == EL_INVISIBLE_WALL ||
6527              element == EL_INVISIBLE_SAND)
6528     {
6529       if (game.lenses_time_left > 0)
6530         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6531
6532       TEST_DrawLevelField(x, y);
6533
6534       // uncrumble neighbour fields, if needed
6535       if (element == EL_INVISIBLE_SAND)
6536         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6537     }
6538     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6539              element == EL_INVISIBLE_WALL_ACTIVE ||
6540              element == EL_INVISIBLE_SAND_ACTIVE)
6541     {
6542       if (game.lenses_time_left == 0)
6543         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6544
6545       TEST_DrawLevelField(x, y);
6546
6547       // re-crumble neighbour fields, if needed
6548       if (element == EL_INVISIBLE_SAND)
6549         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6550     }
6551   }
6552 }
6553
6554 static void RedrawAllInvisibleElementsForMagnifier(void)
6555 {
6556   int x, y;
6557
6558   SCAN_PLAYFIELD(x, y)
6559   {
6560     int element = Tile[x][y];
6561
6562     if (element == EL_EMC_FAKE_GRASS &&
6563         game.magnify_time_left > 0)
6564     {
6565       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6566       TEST_DrawLevelField(x, y);
6567     }
6568     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6569              game.magnify_time_left == 0)
6570     {
6571       Tile[x][y] = EL_EMC_FAKE_GRASS;
6572       TEST_DrawLevelField(x, y);
6573     }
6574     else if (IS_GATE_GRAY(element) &&
6575              game.magnify_time_left > 0)
6576     {
6577       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6578                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6579                     IS_EM_GATE_GRAY(element) ?
6580                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6581                     IS_EMC_GATE_GRAY(element) ?
6582                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6583                     IS_DC_GATE_GRAY(element) ?
6584                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6585                     element);
6586       TEST_DrawLevelField(x, y);
6587     }
6588     else if (IS_GATE_GRAY_ACTIVE(element) &&
6589              game.magnify_time_left == 0)
6590     {
6591       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6592                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6593                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6594                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6595                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6596                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6597                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6598                     EL_DC_GATE_WHITE_GRAY :
6599                     element);
6600       TEST_DrawLevelField(x, y);
6601     }
6602   }
6603 }
6604
6605 static void ToggleLightSwitch(int x, int y)
6606 {
6607   int element = Tile[x][y];
6608
6609   game.light_time_left =
6610     (element == EL_LIGHT_SWITCH ?
6611      level.time_light * FRAMES_PER_SECOND : 0);
6612
6613   RedrawAllLightSwitchesAndInvisibleElements();
6614 }
6615
6616 static void ActivateTimegateSwitch(int x, int y)
6617 {
6618   int xx, yy;
6619
6620   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6621
6622   SCAN_PLAYFIELD(xx, yy)
6623   {
6624     int element = Tile[xx][yy];
6625
6626     if (element == EL_TIMEGATE_CLOSED ||
6627         element == EL_TIMEGATE_CLOSING)
6628     {
6629       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6630       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6631     }
6632
6633     /*
6634     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6635     {
6636       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6637       TEST_DrawLevelField(xx, yy);
6638     }
6639     */
6640
6641   }
6642
6643   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6644                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6645 }
6646
6647 static void Impact(int x, int y)
6648 {
6649   boolean last_line = (y == lev_fieldy - 1);
6650   boolean object_hit = FALSE;
6651   boolean impact = (last_line || object_hit);
6652   int element = Tile[x][y];
6653   int smashed = EL_STEELWALL;
6654
6655   if (!last_line)       // check if element below was hit
6656   {
6657     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6658       return;
6659
6660     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6661                                          MovDir[x][y + 1] != MV_DOWN ||
6662                                          MovPos[x][y + 1] <= TILEY / 2));
6663
6664     // do not smash moving elements that left the smashed field in time
6665     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6666         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6667       object_hit = FALSE;
6668
6669 #if USE_QUICKSAND_IMPACT_BUGFIX
6670     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6671     {
6672       RemoveMovingField(x, y + 1);
6673       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6674       Tile[x][y + 2] = EL_ROCK;
6675       TEST_DrawLevelField(x, y + 2);
6676
6677       object_hit = TRUE;
6678     }
6679
6680     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6681     {
6682       RemoveMovingField(x, y + 1);
6683       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6684       Tile[x][y + 2] = EL_ROCK;
6685       TEST_DrawLevelField(x, y + 2);
6686
6687       object_hit = TRUE;
6688     }
6689 #endif
6690
6691     if (object_hit)
6692       smashed = MovingOrBlocked2Element(x, y + 1);
6693
6694     impact = (last_line || object_hit);
6695   }
6696
6697   if (!last_line && smashed == EL_ACID) // element falls into acid
6698   {
6699     SplashAcid(x, y + 1);
6700     return;
6701   }
6702
6703   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6704   // only reset graphic animation if graphic really changes after impact
6705   if (impact &&
6706       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6707   {
6708     ResetGfxAnimation(x, y);
6709     TEST_DrawLevelField(x, y);
6710   }
6711
6712   if (impact && CAN_EXPLODE_IMPACT(element))
6713   {
6714     Bang(x, y);
6715     return;
6716   }
6717   else if (impact && element == EL_PEARL &&
6718            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6719   {
6720     ResetGfxAnimation(x, y);
6721
6722     Tile[x][y] = EL_PEARL_BREAKING;
6723     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6724     return;
6725   }
6726   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6727   {
6728     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6729
6730     return;
6731   }
6732
6733   if (impact && element == EL_AMOEBA_DROP)
6734   {
6735     if (object_hit && IS_PLAYER(x, y + 1))
6736       KillPlayerUnlessEnemyProtected(x, y + 1);
6737     else if (object_hit && smashed == EL_PENGUIN)
6738       Bang(x, y + 1);
6739     else
6740     {
6741       Tile[x][y] = EL_AMOEBA_GROWING;
6742       Store[x][y] = EL_AMOEBA_WET;
6743
6744       ResetRandomAnimationValue(x, y);
6745     }
6746     return;
6747   }
6748
6749   if (object_hit)               // check which object was hit
6750   {
6751     if ((CAN_PASS_MAGIC_WALL(element) && 
6752          (smashed == EL_MAGIC_WALL ||
6753           smashed == EL_BD_MAGIC_WALL)) ||
6754         (CAN_PASS_DC_MAGIC_WALL(element) &&
6755          smashed == EL_DC_MAGIC_WALL))
6756     {
6757       int xx, yy;
6758       int activated_magic_wall =
6759         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6760          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6761          EL_DC_MAGIC_WALL_ACTIVE);
6762
6763       // activate magic wall / mill
6764       SCAN_PLAYFIELD(xx, yy)
6765       {
6766         if (Tile[xx][yy] == smashed)
6767           Tile[xx][yy] = activated_magic_wall;
6768       }
6769
6770       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6771       game.magic_wall_active = TRUE;
6772
6773       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6774                             SND_MAGIC_WALL_ACTIVATING :
6775                             smashed == EL_BD_MAGIC_WALL ?
6776                             SND_BD_MAGIC_WALL_ACTIVATING :
6777                             SND_DC_MAGIC_WALL_ACTIVATING));
6778     }
6779
6780     if (IS_PLAYER(x, y + 1))
6781     {
6782       if (CAN_SMASH_PLAYER(element))
6783       {
6784         KillPlayerUnlessEnemyProtected(x, y + 1);
6785         return;
6786       }
6787     }
6788     else if (smashed == EL_PENGUIN)
6789     {
6790       if (CAN_SMASH_PLAYER(element))
6791       {
6792         Bang(x, y + 1);
6793         return;
6794       }
6795     }
6796     else if (element == EL_BD_DIAMOND)
6797     {
6798       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6799       {
6800         Bang(x, y + 1);
6801         return;
6802       }
6803     }
6804     else if (((element == EL_SP_INFOTRON ||
6805                element == EL_SP_ZONK) &&
6806               (smashed == EL_SP_SNIKSNAK ||
6807                smashed == EL_SP_ELECTRON ||
6808                smashed == EL_SP_DISK_ORANGE)) ||
6809              (element == EL_SP_INFOTRON &&
6810               smashed == EL_SP_DISK_YELLOW))
6811     {
6812       Bang(x, y + 1);
6813       return;
6814     }
6815     else if (CAN_SMASH_EVERYTHING(element))
6816     {
6817       if (IS_CLASSIC_ENEMY(smashed) ||
6818           CAN_EXPLODE_SMASHED(smashed))
6819       {
6820         Bang(x, y + 1);
6821         return;
6822       }
6823       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6824       {
6825         if (smashed == EL_LAMP ||
6826             smashed == EL_LAMP_ACTIVE)
6827         {
6828           Bang(x, y + 1);
6829           return;
6830         }
6831         else if (smashed == EL_NUT)
6832         {
6833           Tile[x][y + 1] = EL_NUT_BREAKING;
6834           PlayLevelSound(x, y, SND_NUT_BREAKING);
6835           RaiseScoreElement(EL_NUT);
6836           return;
6837         }
6838         else if (smashed == EL_PEARL)
6839         {
6840           ResetGfxAnimation(x, y);
6841
6842           Tile[x][y + 1] = EL_PEARL_BREAKING;
6843           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6844           return;
6845         }
6846         else if (smashed == EL_DIAMOND)
6847         {
6848           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6849           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6850           return;
6851         }
6852         else if (IS_BELT_SWITCH(smashed))
6853         {
6854           ToggleBeltSwitch(x, y + 1);
6855         }
6856         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6857                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6858                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6859                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6860         {
6861           ToggleSwitchgateSwitch(x, y + 1);
6862         }
6863         else if (smashed == EL_LIGHT_SWITCH ||
6864                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6865         {
6866           ToggleLightSwitch(x, y + 1);
6867         }
6868         else
6869         {
6870           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6871
6872           CheckElementChangeBySide(x, y + 1, smashed, element,
6873                                    CE_SWITCHED, CH_SIDE_TOP);
6874           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6875                                             CH_SIDE_TOP);
6876         }
6877       }
6878       else
6879       {
6880         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6881       }
6882     }
6883   }
6884
6885   // play sound of magic wall / mill
6886   if (!last_line &&
6887       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6888        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6889        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6890   {
6891     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6892       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6893     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6894       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6895     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6896       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6897
6898     return;
6899   }
6900
6901   // play sound of object that hits the ground
6902   if (last_line || object_hit)
6903     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6904 }
6905
6906 static void TurnRoundExt(int x, int y)
6907 {
6908   static struct
6909   {
6910     int dx, dy;
6911   } move_xy[] =
6912   {
6913     {  0,  0 },
6914     { -1,  0 },
6915     { +1,  0 },
6916     {  0,  0 },
6917     {  0, -1 },
6918     {  0,  0 }, { 0, 0 }, { 0, 0 },
6919     {  0, +1 }
6920   };
6921   static struct
6922   {
6923     int left, right, back;
6924   } turn[] =
6925   {
6926     { 0,        0,              0        },
6927     { MV_DOWN,  MV_UP,          MV_RIGHT },
6928     { MV_UP,    MV_DOWN,        MV_LEFT  },
6929     { 0,        0,              0        },
6930     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6931     { 0,        0,              0        },
6932     { 0,        0,              0        },
6933     { 0,        0,              0        },
6934     { MV_RIGHT, MV_LEFT,        MV_UP    }
6935   };
6936
6937   int element = Tile[x][y];
6938   int move_pattern = element_info[element].move_pattern;
6939
6940   int old_move_dir = MovDir[x][y];
6941   int left_dir  = turn[old_move_dir].left;
6942   int right_dir = turn[old_move_dir].right;
6943   int back_dir  = turn[old_move_dir].back;
6944
6945   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6946   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6947   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6948   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6949
6950   int left_x  = x + left_dx,  left_y  = y + left_dy;
6951   int right_x = x + right_dx, right_y = y + right_dy;
6952   int move_x  = x + move_dx,  move_y  = y + move_dy;
6953
6954   int xx, yy;
6955
6956   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6957   {
6958     TestIfBadThingTouchesOtherBadThing(x, y);
6959
6960     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6961       MovDir[x][y] = right_dir;
6962     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6963       MovDir[x][y] = left_dir;
6964
6965     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6966       MovDelay[x][y] = 9;
6967     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6968       MovDelay[x][y] = 1;
6969   }
6970   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6971   {
6972     TestIfBadThingTouchesOtherBadThing(x, y);
6973
6974     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6975       MovDir[x][y] = left_dir;
6976     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6977       MovDir[x][y] = right_dir;
6978
6979     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6980       MovDelay[x][y] = 9;
6981     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6982       MovDelay[x][y] = 1;
6983   }
6984   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6985   {
6986     TestIfBadThingTouchesOtherBadThing(x, y);
6987
6988     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6989       MovDir[x][y] = left_dir;
6990     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6991       MovDir[x][y] = right_dir;
6992
6993     if (MovDir[x][y] != old_move_dir)
6994       MovDelay[x][y] = 9;
6995   }
6996   else if (element == EL_YAMYAM)
6997   {
6998     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6999     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7000
7001     if (can_turn_left && can_turn_right)
7002       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7003     else if (can_turn_left)
7004       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7005     else if (can_turn_right)
7006       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7007     else
7008       MovDir[x][y] = back_dir;
7009
7010     MovDelay[x][y] = 16 + 16 * RND(3);
7011   }
7012   else if (element == EL_DARK_YAMYAM)
7013   {
7014     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7015                                                          left_x, left_y);
7016     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7017                                                          right_x, right_y);
7018
7019     if (can_turn_left && can_turn_right)
7020       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7021     else if (can_turn_left)
7022       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7023     else if (can_turn_right)
7024       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7025     else
7026       MovDir[x][y] = back_dir;
7027
7028     MovDelay[x][y] = 16 + 16 * RND(3);
7029   }
7030   else if (element == EL_PACMAN)
7031   {
7032     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7033     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7034
7035     if (can_turn_left && can_turn_right)
7036       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7037     else if (can_turn_left)
7038       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7039     else if (can_turn_right)
7040       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7041     else
7042       MovDir[x][y] = back_dir;
7043
7044     MovDelay[x][y] = 6 + RND(40);
7045   }
7046   else if (element == EL_PIG)
7047   {
7048     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7049     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7050     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7051     boolean should_turn_left, should_turn_right, should_move_on;
7052     int rnd_value = 24;
7053     int rnd = RND(rnd_value);
7054
7055     should_turn_left = (can_turn_left &&
7056                         (!can_move_on ||
7057                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7058                                                    y + back_dy + left_dy)));
7059     should_turn_right = (can_turn_right &&
7060                          (!can_move_on ||
7061                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7062                                                     y + back_dy + right_dy)));
7063     should_move_on = (can_move_on &&
7064                       (!can_turn_left ||
7065                        !can_turn_right ||
7066                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7067                                                  y + move_dy + left_dy) ||
7068                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7069                                                  y + move_dy + right_dy)));
7070
7071     if (should_turn_left || should_turn_right || should_move_on)
7072     {
7073       if (should_turn_left && should_turn_right && should_move_on)
7074         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7075                         rnd < 2 * rnd_value / 3 ? right_dir :
7076                         old_move_dir);
7077       else if (should_turn_left && should_turn_right)
7078         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7079       else if (should_turn_left && should_move_on)
7080         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7081       else if (should_turn_right && should_move_on)
7082         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7083       else if (should_turn_left)
7084         MovDir[x][y] = left_dir;
7085       else if (should_turn_right)
7086         MovDir[x][y] = right_dir;
7087       else if (should_move_on)
7088         MovDir[x][y] = old_move_dir;
7089     }
7090     else if (can_move_on && rnd > rnd_value / 8)
7091       MovDir[x][y] = old_move_dir;
7092     else if (can_turn_left && can_turn_right)
7093       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7094     else if (can_turn_left && rnd > rnd_value / 8)
7095       MovDir[x][y] = left_dir;
7096     else if (can_turn_right && rnd > rnd_value/8)
7097       MovDir[x][y] = right_dir;
7098     else
7099       MovDir[x][y] = back_dir;
7100
7101     xx = x + move_xy[MovDir[x][y]].dx;
7102     yy = y + move_xy[MovDir[x][y]].dy;
7103
7104     if (!IN_LEV_FIELD(xx, yy) ||
7105         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7106       MovDir[x][y] = old_move_dir;
7107
7108     MovDelay[x][y] = 0;
7109   }
7110   else if (element == EL_DRAGON)
7111   {
7112     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7113     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7114     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7115     int rnd_value = 24;
7116     int rnd = RND(rnd_value);
7117
7118     if (can_move_on && rnd > rnd_value / 8)
7119       MovDir[x][y] = old_move_dir;
7120     else if (can_turn_left && can_turn_right)
7121       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7122     else if (can_turn_left && rnd > rnd_value / 8)
7123       MovDir[x][y] = left_dir;
7124     else if (can_turn_right && rnd > rnd_value / 8)
7125       MovDir[x][y] = right_dir;
7126     else
7127       MovDir[x][y] = back_dir;
7128
7129     xx = x + move_xy[MovDir[x][y]].dx;
7130     yy = y + move_xy[MovDir[x][y]].dy;
7131
7132     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7133       MovDir[x][y] = old_move_dir;
7134
7135     MovDelay[x][y] = 0;
7136   }
7137   else if (element == EL_MOLE)
7138   {
7139     boolean can_move_on =
7140       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7141                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7142                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7143     if (!can_move_on)
7144     {
7145       boolean can_turn_left =
7146         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7147                               IS_AMOEBOID(Tile[left_x][left_y])));
7148
7149       boolean can_turn_right =
7150         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7151                               IS_AMOEBOID(Tile[right_x][right_y])));
7152
7153       if (can_turn_left && can_turn_right)
7154         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7155       else if (can_turn_left)
7156         MovDir[x][y] = left_dir;
7157       else
7158         MovDir[x][y] = right_dir;
7159     }
7160
7161     if (MovDir[x][y] != old_move_dir)
7162       MovDelay[x][y] = 9;
7163   }
7164   else if (element == EL_BALLOON)
7165   {
7166     MovDir[x][y] = game.wind_direction;
7167     MovDelay[x][y] = 0;
7168   }
7169   else if (element == EL_SPRING)
7170   {
7171     if (MovDir[x][y] & MV_HORIZONTAL)
7172     {
7173       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7174           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7175       {
7176         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7177         ResetGfxAnimation(move_x, move_y);
7178         TEST_DrawLevelField(move_x, move_y);
7179
7180         MovDir[x][y] = back_dir;
7181       }
7182       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7183                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7184         MovDir[x][y] = MV_NONE;
7185     }
7186
7187     MovDelay[x][y] = 0;
7188   }
7189   else if (element == EL_ROBOT ||
7190            element == EL_SATELLITE ||
7191            element == EL_PENGUIN ||
7192            element == EL_EMC_ANDROID)
7193   {
7194     int attr_x = -1, attr_y = -1;
7195
7196     if (game.all_players_gone)
7197     {
7198       attr_x = game.exit_x;
7199       attr_y = game.exit_y;
7200     }
7201     else
7202     {
7203       int i;
7204
7205       for (i = 0; i < MAX_PLAYERS; i++)
7206       {
7207         struct PlayerInfo *player = &stored_player[i];
7208         int jx = player->jx, jy = player->jy;
7209
7210         if (!player->active)
7211           continue;
7212
7213         if (attr_x == -1 ||
7214             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7215         {
7216           attr_x = jx;
7217           attr_y = jy;
7218         }
7219       }
7220     }
7221
7222     if (element == EL_ROBOT &&
7223         game.robot_wheel_x >= 0 &&
7224         game.robot_wheel_y >= 0 &&
7225         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7226          game.engine_version < VERSION_IDENT(3,1,0,0)))
7227     {
7228       attr_x = game.robot_wheel_x;
7229       attr_y = game.robot_wheel_y;
7230     }
7231
7232     if (element == EL_PENGUIN)
7233     {
7234       int i;
7235       static int xy[4][2] =
7236       {
7237         { 0, -1 },
7238         { -1, 0 },
7239         { +1, 0 },
7240         { 0, +1 }
7241       };
7242
7243       for (i = 0; i < NUM_DIRECTIONS; i++)
7244       {
7245         int ex = x + xy[i][0];
7246         int ey = y + xy[i][1];
7247
7248         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7249                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7250                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7251                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7252         {
7253           attr_x = ex;
7254           attr_y = ey;
7255           break;
7256         }
7257       }
7258     }
7259
7260     MovDir[x][y] = MV_NONE;
7261     if (attr_x < x)
7262       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7263     else if (attr_x > x)
7264       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7265     if (attr_y < y)
7266       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7267     else if (attr_y > y)
7268       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7269
7270     if (element == EL_ROBOT)
7271     {
7272       int newx, newy;
7273
7274       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7275         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7276       Moving2Blocked(x, y, &newx, &newy);
7277
7278       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7279         MovDelay[x][y] = 8 + 8 * !RND(3);
7280       else
7281         MovDelay[x][y] = 16;
7282     }
7283     else if (element == EL_PENGUIN)
7284     {
7285       int newx, newy;
7286
7287       MovDelay[x][y] = 1;
7288
7289       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7290       {
7291         boolean first_horiz = RND(2);
7292         int new_move_dir = MovDir[x][y];
7293
7294         MovDir[x][y] =
7295           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7296         Moving2Blocked(x, y, &newx, &newy);
7297
7298         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7299           return;
7300
7301         MovDir[x][y] =
7302           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7303         Moving2Blocked(x, y, &newx, &newy);
7304
7305         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7306           return;
7307
7308         MovDir[x][y] = old_move_dir;
7309         return;
7310       }
7311     }
7312     else if (element == EL_SATELLITE)
7313     {
7314       int newx, newy;
7315
7316       MovDelay[x][y] = 1;
7317
7318       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7319       {
7320         boolean first_horiz = RND(2);
7321         int new_move_dir = MovDir[x][y];
7322
7323         MovDir[x][y] =
7324           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7325         Moving2Blocked(x, y, &newx, &newy);
7326
7327         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7328           return;
7329
7330         MovDir[x][y] =
7331           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7332         Moving2Blocked(x, y, &newx, &newy);
7333
7334         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7335           return;
7336
7337         MovDir[x][y] = old_move_dir;
7338         return;
7339       }
7340     }
7341     else if (element == EL_EMC_ANDROID)
7342     {
7343       static int check_pos[16] =
7344       {
7345         -1,             //  0 => (invalid)
7346         7,              //  1 => MV_LEFT
7347         3,              //  2 => MV_RIGHT
7348         -1,             //  3 => (invalid)
7349         1,              //  4 =>            MV_UP
7350         0,              //  5 => MV_LEFT  | MV_UP
7351         2,              //  6 => MV_RIGHT | MV_UP
7352         -1,             //  7 => (invalid)
7353         5,              //  8 =>            MV_DOWN
7354         6,              //  9 => MV_LEFT  | MV_DOWN
7355         4,              // 10 => MV_RIGHT | MV_DOWN
7356         -1,             // 11 => (invalid)
7357         -1,             // 12 => (invalid)
7358         -1,             // 13 => (invalid)
7359         -1,             // 14 => (invalid)
7360         -1,             // 15 => (invalid)
7361       };
7362       static struct
7363       {
7364         int dx, dy;
7365         int dir;
7366       } check_xy[8] =
7367       {
7368         { -1, -1,       MV_LEFT  | MV_UP   },
7369         {  0, -1,                  MV_UP   },
7370         { +1, -1,       MV_RIGHT | MV_UP   },
7371         { +1,  0,       MV_RIGHT           },
7372         { +1, +1,       MV_RIGHT | MV_DOWN },
7373         {  0, +1,                  MV_DOWN },
7374         { -1, +1,       MV_LEFT  | MV_DOWN },
7375         { -1,  0,       MV_LEFT            },
7376       };
7377       int start_pos, check_order;
7378       boolean can_clone = FALSE;
7379       int i;
7380
7381       // check if there is any free field around current position
7382       for (i = 0; i < 8; i++)
7383       {
7384         int newx = x + check_xy[i].dx;
7385         int newy = y + check_xy[i].dy;
7386
7387         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7388         {
7389           can_clone = TRUE;
7390
7391           break;
7392         }
7393       }
7394
7395       if (can_clone)            // randomly find an element to clone
7396       {
7397         can_clone = FALSE;
7398
7399         start_pos = check_pos[RND(8)];
7400         check_order = (RND(2) ? -1 : +1);
7401
7402         for (i = 0; i < 8; i++)
7403         {
7404           int pos_raw = start_pos + i * check_order;
7405           int pos = (pos_raw + 8) % 8;
7406           int newx = x + check_xy[pos].dx;
7407           int newy = y + check_xy[pos].dy;
7408
7409           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7410           {
7411             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7412             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7413
7414             Store[x][y] = Tile[newx][newy];
7415
7416             can_clone = TRUE;
7417
7418             break;
7419           }
7420         }
7421       }
7422
7423       if (can_clone)            // randomly find a direction to move
7424       {
7425         can_clone = FALSE;
7426
7427         start_pos = check_pos[RND(8)];
7428         check_order = (RND(2) ? -1 : +1);
7429
7430         for (i = 0; i < 8; i++)
7431         {
7432           int pos_raw = start_pos + i * check_order;
7433           int pos = (pos_raw + 8) % 8;
7434           int newx = x + check_xy[pos].dx;
7435           int newy = y + check_xy[pos].dy;
7436           int new_move_dir = check_xy[pos].dir;
7437
7438           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7439           {
7440             MovDir[x][y] = new_move_dir;
7441             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7442
7443             can_clone = TRUE;
7444
7445             break;
7446           }
7447         }
7448       }
7449
7450       if (can_clone)            // cloning and moving successful
7451         return;
7452
7453       // cannot clone -- try to move towards player
7454
7455       start_pos = check_pos[MovDir[x][y] & 0x0f];
7456       check_order = (RND(2) ? -1 : +1);
7457
7458       for (i = 0; i < 3; i++)
7459       {
7460         // first check start_pos, then previous/next or (next/previous) pos
7461         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7462         int pos = (pos_raw + 8) % 8;
7463         int newx = x + check_xy[pos].dx;
7464         int newy = y + check_xy[pos].dy;
7465         int new_move_dir = check_xy[pos].dir;
7466
7467         if (IS_PLAYER(newx, newy))
7468           break;
7469
7470         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7471         {
7472           MovDir[x][y] = new_move_dir;
7473           MovDelay[x][y] = level.android_move_time * 8 + 1;
7474
7475           break;
7476         }
7477       }
7478     }
7479   }
7480   else if (move_pattern == MV_TURNING_LEFT ||
7481            move_pattern == MV_TURNING_RIGHT ||
7482            move_pattern == MV_TURNING_LEFT_RIGHT ||
7483            move_pattern == MV_TURNING_RIGHT_LEFT ||
7484            move_pattern == MV_TURNING_RANDOM ||
7485            move_pattern == MV_ALL_DIRECTIONS)
7486   {
7487     boolean can_turn_left =
7488       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7489     boolean can_turn_right =
7490       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7491
7492     if (element_info[element].move_stepsize == 0)       // "not moving"
7493       return;
7494
7495     if (move_pattern == MV_TURNING_LEFT)
7496       MovDir[x][y] = left_dir;
7497     else if (move_pattern == MV_TURNING_RIGHT)
7498       MovDir[x][y] = right_dir;
7499     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7500       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7501     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7502       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7503     else if (move_pattern == MV_TURNING_RANDOM)
7504       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7505                       can_turn_right && !can_turn_left ? right_dir :
7506                       RND(2) ? left_dir : right_dir);
7507     else if (can_turn_left && can_turn_right)
7508       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7509     else if (can_turn_left)
7510       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7511     else if (can_turn_right)
7512       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7513     else
7514       MovDir[x][y] = back_dir;
7515
7516     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7517   }
7518   else if (move_pattern == MV_HORIZONTAL ||
7519            move_pattern == MV_VERTICAL)
7520   {
7521     if (move_pattern & old_move_dir)
7522       MovDir[x][y] = back_dir;
7523     else if (move_pattern == MV_HORIZONTAL)
7524       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7525     else if (move_pattern == MV_VERTICAL)
7526       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7527
7528     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7529   }
7530   else if (move_pattern & MV_ANY_DIRECTION)
7531   {
7532     MovDir[x][y] = move_pattern;
7533     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7534   }
7535   else if (move_pattern & MV_WIND_DIRECTION)
7536   {
7537     MovDir[x][y] = game.wind_direction;
7538     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7539   }
7540   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7541   {
7542     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7543       MovDir[x][y] = left_dir;
7544     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7545       MovDir[x][y] = right_dir;
7546
7547     if (MovDir[x][y] != old_move_dir)
7548       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7549   }
7550   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7551   {
7552     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7553       MovDir[x][y] = right_dir;
7554     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7555       MovDir[x][y] = left_dir;
7556
7557     if (MovDir[x][y] != old_move_dir)
7558       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7559   }
7560   else if (move_pattern == MV_TOWARDS_PLAYER ||
7561            move_pattern == MV_AWAY_FROM_PLAYER)
7562   {
7563     int attr_x = -1, attr_y = -1;
7564     int newx, newy;
7565     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7566
7567     if (game.all_players_gone)
7568     {
7569       attr_x = game.exit_x;
7570       attr_y = game.exit_y;
7571     }
7572     else
7573     {
7574       int i;
7575
7576       for (i = 0; i < MAX_PLAYERS; i++)
7577       {
7578         struct PlayerInfo *player = &stored_player[i];
7579         int jx = player->jx, jy = player->jy;
7580
7581         if (!player->active)
7582           continue;
7583
7584         if (attr_x == -1 ||
7585             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7586         {
7587           attr_x = jx;
7588           attr_y = jy;
7589         }
7590       }
7591     }
7592
7593     MovDir[x][y] = MV_NONE;
7594     if (attr_x < x)
7595       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7596     else if (attr_x > x)
7597       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7598     if (attr_y < y)
7599       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7600     else if (attr_y > y)
7601       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7602
7603     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7604
7605     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7606     {
7607       boolean first_horiz = RND(2);
7608       int new_move_dir = MovDir[x][y];
7609
7610       if (element_info[element].move_stepsize == 0)     // "not moving"
7611       {
7612         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7613         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7614
7615         return;
7616       }
7617
7618       MovDir[x][y] =
7619         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7620       Moving2Blocked(x, y, &newx, &newy);
7621
7622       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7623         return;
7624
7625       MovDir[x][y] =
7626         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7627       Moving2Blocked(x, y, &newx, &newy);
7628
7629       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7630         return;
7631
7632       MovDir[x][y] = old_move_dir;
7633     }
7634   }
7635   else if (move_pattern == MV_WHEN_PUSHED ||
7636            move_pattern == MV_WHEN_DROPPED)
7637   {
7638     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7639       MovDir[x][y] = MV_NONE;
7640
7641     MovDelay[x][y] = 0;
7642   }
7643   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7644   {
7645     static int test_xy[7][2] =
7646     {
7647       { 0, -1 },
7648       { -1, 0 },
7649       { +1, 0 },
7650       { 0, +1 },
7651       { 0, -1 },
7652       { -1, 0 },
7653       { +1, 0 },
7654     };
7655     static int test_dir[7] =
7656     {
7657       MV_UP,
7658       MV_LEFT,
7659       MV_RIGHT,
7660       MV_DOWN,
7661       MV_UP,
7662       MV_LEFT,
7663       MV_RIGHT,
7664     };
7665     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7666     int move_preference = -1000000;     // start with very low preference
7667     int new_move_dir = MV_NONE;
7668     int start_test = RND(4);
7669     int i;
7670
7671     for (i = 0; i < NUM_DIRECTIONS; i++)
7672     {
7673       int move_dir = test_dir[start_test + i];
7674       int move_dir_preference;
7675
7676       xx = x + test_xy[start_test + i][0];
7677       yy = y + test_xy[start_test + i][1];
7678
7679       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7680           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7681       {
7682         new_move_dir = move_dir;
7683
7684         break;
7685       }
7686
7687       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7688         continue;
7689
7690       move_dir_preference = -1 * RunnerVisit[xx][yy];
7691       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7692         move_dir_preference = PlayerVisit[xx][yy];
7693
7694       if (move_dir_preference > move_preference)
7695       {
7696         // prefer field that has not been visited for the longest time
7697         move_preference = move_dir_preference;
7698         new_move_dir = move_dir;
7699       }
7700       else if (move_dir_preference == move_preference &&
7701                move_dir == old_move_dir)
7702       {
7703         // prefer last direction when all directions are preferred equally
7704         move_preference = move_dir_preference;
7705         new_move_dir = move_dir;
7706       }
7707     }
7708
7709     MovDir[x][y] = new_move_dir;
7710     if (old_move_dir != new_move_dir)
7711       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7712   }
7713 }
7714
7715 static void TurnRound(int x, int y)
7716 {
7717   int direction = MovDir[x][y];
7718
7719   TurnRoundExt(x, y);
7720
7721   GfxDir[x][y] = MovDir[x][y];
7722
7723   if (direction != MovDir[x][y])
7724     GfxFrame[x][y] = 0;
7725
7726   if (MovDelay[x][y])
7727     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7728
7729   ResetGfxFrame(x, y);
7730 }
7731
7732 static boolean JustBeingPushed(int x, int y)
7733 {
7734   int i;
7735
7736   for (i = 0; i < MAX_PLAYERS; i++)
7737   {
7738     struct PlayerInfo *player = &stored_player[i];
7739
7740     if (player->active && player->is_pushing && player->MovPos)
7741     {
7742       int next_jx = player->jx + (player->jx - player->last_jx);
7743       int next_jy = player->jy + (player->jy - player->last_jy);
7744
7745       if (x == next_jx && y == next_jy)
7746         return TRUE;
7747     }
7748   }
7749
7750   return FALSE;
7751 }
7752
7753 static void StartMoving(int x, int y)
7754 {
7755   boolean started_moving = FALSE;       // some elements can fall _and_ move
7756   int element = Tile[x][y];
7757
7758   if (Stop[x][y])
7759     return;
7760
7761   if (MovDelay[x][y] == 0)
7762     GfxAction[x][y] = ACTION_DEFAULT;
7763
7764   if (CAN_FALL(element) && y < lev_fieldy - 1)
7765   {
7766     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7767         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7768       if (JustBeingPushed(x, y))
7769         return;
7770
7771     if (element == EL_QUICKSAND_FULL)
7772     {
7773       if (IS_FREE(x, y + 1))
7774       {
7775         InitMovingField(x, y, MV_DOWN);
7776         started_moving = TRUE;
7777
7778         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7779 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7780         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7781           Store[x][y] = EL_ROCK;
7782 #else
7783         Store[x][y] = EL_ROCK;
7784 #endif
7785
7786         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7787       }
7788       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7789       {
7790         if (!MovDelay[x][y])
7791         {
7792           MovDelay[x][y] = TILEY + 1;
7793
7794           ResetGfxAnimation(x, y);
7795           ResetGfxAnimation(x, y + 1);
7796         }
7797
7798         if (MovDelay[x][y])
7799         {
7800           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7801           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7802
7803           MovDelay[x][y]--;
7804           if (MovDelay[x][y])
7805             return;
7806         }
7807
7808         Tile[x][y] = EL_QUICKSAND_EMPTY;
7809         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7810         Store[x][y + 1] = Store[x][y];
7811         Store[x][y] = 0;
7812
7813         PlayLevelSoundAction(x, y, ACTION_FILLING);
7814       }
7815       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7816       {
7817         if (!MovDelay[x][y])
7818         {
7819           MovDelay[x][y] = TILEY + 1;
7820
7821           ResetGfxAnimation(x, y);
7822           ResetGfxAnimation(x, y + 1);
7823         }
7824
7825         if (MovDelay[x][y])
7826         {
7827           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7828           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7829
7830           MovDelay[x][y]--;
7831           if (MovDelay[x][y])
7832             return;
7833         }
7834
7835         Tile[x][y] = EL_QUICKSAND_EMPTY;
7836         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7837         Store[x][y + 1] = Store[x][y];
7838         Store[x][y] = 0;
7839
7840         PlayLevelSoundAction(x, y, ACTION_FILLING);
7841       }
7842     }
7843     else if (element == EL_QUICKSAND_FAST_FULL)
7844     {
7845       if (IS_FREE(x, y + 1))
7846       {
7847         InitMovingField(x, y, MV_DOWN);
7848         started_moving = TRUE;
7849
7850         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7851 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7852         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7853           Store[x][y] = EL_ROCK;
7854 #else
7855         Store[x][y] = EL_ROCK;
7856 #endif
7857
7858         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7859       }
7860       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7861       {
7862         if (!MovDelay[x][y])
7863         {
7864           MovDelay[x][y] = TILEY + 1;
7865
7866           ResetGfxAnimation(x, y);
7867           ResetGfxAnimation(x, y + 1);
7868         }
7869
7870         if (MovDelay[x][y])
7871         {
7872           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7873           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7874
7875           MovDelay[x][y]--;
7876           if (MovDelay[x][y])
7877             return;
7878         }
7879
7880         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7881         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7882         Store[x][y + 1] = Store[x][y];
7883         Store[x][y] = 0;
7884
7885         PlayLevelSoundAction(x, y, ACTION_FILLING);
7886       }
7887       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7888       {
7889         if (!MovDelay[x][y])
7890         {
7891           MovDelay[x][y] = TILEY + 1;
7892
7893           ResetGfxAnimation(x, y);
7894           ResetGfxAnimation(x, y + 1);
7895         }
7896
7897         if (MovDelay[x][y])
7898         {
7899           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7900           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7901
7902           MovDelay[x][y]--;
7903           if (MovDelay[x][y])
7904             return;
7905         }
7906
7907         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7908         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7909         Store[x][y + 1] = Store[x][y];
7910         Store[x][y] = 0;
7911
7912         PlayLevelSoundAction(x, y, ACTION_FILLING);
7913       }
7914     }
7915     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7916              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7917     {
7918       InitMovingField(x, y, MV_DOWN);
7919       started_moving = TRUE;
7920
7921       Tile[x][y] = EL_QUICKSAND_FILLING;
7922       Store[x][y] = element;
7923
7924       PlayLevelSoundAction(x, y, ACTION_FILLING);
7925     }
7926     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7927              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7928     {
7929       InitMovingField(x, y, MV_DOWN);
7930       started_moving = TRUE;
7931
7932       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7933       Store[x][y] = element;
7934
7935       PlayLevelSoundAction(x, y, ACTION_FILLING);
7936     }
7937     else if (element == EL_MAGIC_WALL_FULL)
7938     {
7939       if (IS_FREE(x, y + 1))
7940       {
7941         InitMovingField(x, y, MV_DOWN);
7942         started_moving = TRUE;
7943
7944         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7945         Store[x][y] = EL_CHANGED(Store[x][y]);
7946       }
7947       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7948       {
7949         if (!MovDelay[x][y])
7950           MovDelay[x][y] = TILEY / 4 + 1;
7951
7952         if (MovDelay[x][y])
7953         {
7954           MovDelay[x][y]--;
7955           if (MovDelay[x][y])
7956             return;
7957         }
7958
7959         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7960         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7961         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7962         Store[x][y] = 0;
7963       }
7964     }
7965     else if (element == EL_BD_MAGIC_WALL_FULL)
7966     {
7967       if (IS_FREE(x, y + 1))
7968       {
7969         InitMovingField(x, y, MV_DOWN);
7970         started_moving = TRUE;
7971
7972         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7973         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7974       }
7975       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7976       {
7977         if (!MovDelay[x][y])
7978           MovDelay[x][y] = TILEY / 4 + 1;
7979
7980         if (MovDelay[x][y])
7981         {
7982           MovDelay[x][y]--;
7983           if (MovDelay[x][y])
7984             return;
7985         }
7986
7987         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7988         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7989         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7990         Store[x][y] = 0;
7991       }
7992     }
7993     else if (element == EL_DC_MAGIC_WALL_FULL)
7994     {
7995       if (IS_FREE(x, y + 1))
7996       {
7997         InitMovingField(x, y, MV_DOWN);
7998         started_moving = TRUE;
7999
8000         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8001         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8002       }
8003       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8004       {
8005         if (!MovDelay[x][y])
8006           MovDelay[x][y] = TILEY / 4 + 1;
8007
8008         if (MovDelay[x][y])
8009         {
8010           MovDelay[x][y]--;
8011           if (MovDelay[x][y])
8012             return;
8013         }
8014
8015         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8016         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8017         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8018         Store[x][y] = 0;
8019       }
8020     }
8021     else if ((CAN_PASS_MAGIC_WALL(element) &&
8022               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8023                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8024              (CAN_PASS_DC_MAGIC_WALL(element) &&
8025               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8026
8027     {
8028       InitMovingField(x, y, MV_DOWN);
8029       started_moving = TRUE;
8030
8031       Tile[x][y] =
8032         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8033          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8034          EL_DC_MAGIC_WALL_FILLING);
8035       Store[x][y] = element;
8036     }
8037     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8038     {
8039       SplashAcid(x, y + 1);
8040
8041       InitMovingField(x, y, MV_DOWN);
8042       started_moving = TRUE;
8043
8044       Store[x][y] = EL_ACID;
8045     }
8046     else if (
8047              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8048               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8049              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8050               CAN_FALL(element) && WasJustFalling[x][y] &&
8051               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8052
8053              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8054               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8055               (Tile[x][y + 1] == EL_BLOCKED)))
8056     {
8057       /* this is needed for a special case not covered by calling "Impact()"
8058          from "ContinueMoving()": if an element moves to a tile directly below
8059          another element which was just falling on that tile (which was empty
8060          in the previous frame), the falling element above would just stop
8061          instead of smashing the element below (in previous version, the above
8062          element was just checked for "moving" instead of "falling", resulting
8063          in incorrect smashes caused by horizontal movement of the above
8064          element; also, the case of the player being the element to smash was
8065          simply not covered here... :-/ ) */
8066
8067       CheckCollision[x][y] = 0;
8068       CheckImpact[x][y] = 0;
8069
8070       Impact(x, y);
8071     }
8072     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8073     {
8074       if (MovDir[x][y] == MV_NONE)
8075       {
8076         InitMovingField(x, y, MV_DOWN);
8077         started_moving = TRUE;
8078       }
8079     }
8080     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8081     {
8082       if (WasJustFalling[x][y]) // prevent animation from being restarted
8083         MovDir[x][y] = MV_DOWN;
8084
8085       InitMovingField(x, y, MV_DOWN);
8086       started_moving = TRUE;
8087     }
8088     else if (element == EL_AMOEBA_DROP)
8089     {
8090       Tile[x][y] = EL_AMOEBA_GROWING;
8091       Store[x][y] = EL_AMOEBA_WET;
8092     }
8093     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8094               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8095              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8096              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8097     {
8098       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8099                                 (IS_FREE(x - 1, y + 1) ||
8100                                  Tile[x - 1][y + 1] == EL_ACID));
8101       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8102                                 (IS_FREE(x + 1, y + 1) ||
8103                                  Tile[x + 1][y + 1] == EL_ACID));
8104       boolean can_fall_any  = (can_fall_left || can_fall_right);
8105       boolean can_fall_both = (can_fall_left && can_fall_right);
8106       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8107
8108       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8109       {
8110         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8111           can_fall_right = FALSE;
8112         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8113           can_fall_left = FALSE;
8114         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8115           can_fall_right = FALSE;
8116         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8117           can_fall_left = FALSE;
8118
8119         can_fall_any  = (can_fall_left || can_fall_right);
8120         can_fall_both = FALSE;
8121       }
8122
8123       if (can_fall_both)
8124       {
8125         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8126           can_fall_right = FALSE;       // slip down on left side
8127         else
8128           can_fall_left = !(can_fall_right = RND(2));
8129
8130         can_fall_both = FALSE;
8131       }
8132
8133       if (can_fall_any)
8134       {
8135         // if not determined otherwise, prefer left side for slipping down
8136         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8137         started_moving = TRUE;
8138       }
8139     }
8140     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8141     {
8142       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8143       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8144       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8145       int belt_dir = game.belt_dir[belt_nr];
8146
8147       if ((belt_dir == MV_LEFT  && left_is_free) ||
8148           (belt_dir == MV_RIGHT && right_is_free))
8149       {
8150         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8151
8152         InitMovingField(x, y, belt_dir);
8153         started_moving = TRUE;
8154
8155         Pushed[x][y] = TRUE;
8156         Pushed[nextx][y] = TRUE;
8157
8158         GfxAction[x][y] = ACTION_DEFAULT;
8159       }
8160       else
8161       {
8162         MovDir[x][y] = 0;       // if element was moving, stop it
8163       }
8164     }
8165   }
8166
8167   // not "else if" because of elements that can fall and move (EL_SPRING)
8168   if (CAN_MOVE(element) && !started_moving)
8169   {
8170     int move_pattern = element_info[element].move_pattern;
8171     int newx, newy;
8172
8173     Moving2Blocked(x, y, &newx, &newy);
8174
8175     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8176       return;
8177
8178     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8179         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8180     {
8181       WasJustMoving[x][y] = 0;
8182       CheckCollision[x][y] = 0;
8183
8184       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8185
8186       if (Tile[x][y] != element)        // element has changed
8187         return;
8188     }
8189
8190     if (!MovDelay[x][y])        // start new movement phase
8191     {
8192       // all objects that can change their move direction after each step
8193       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8194
8195       if (element != EL_YAMYAM &&
8196           element != EL_DARK_YAMYAM &&
8197           element != EL_PACMAN &&
8198           !(move_pattern & MV_ANY_DIRECTION) &&
8199           move_pattern != MV_TURNING_LEFT &&
8200           move_pattern != MV_TURNING_RIGHT &&
8201           move_pattern != MV_TURNING_LEFT_RIGHT &&
8202           move_pattern != MV_TURNING_RIGHT_LEFT &&
8203           move_pattern != MV_TURNING_RANDOM)
8204       {
8205         TurnRound(x, y);
8206
8207         if (MovDelay[x][y] && (element == EL_BUG ||
8208                                element == EL_SPACESHIP ||
8209                                element == EL_SP_SNIKSNAK ||
8210                                element == EL_SP_ELECTRON ||
8211                                element == EL_MOLE))
8212           TEST_DrawLevelField(x, y);
8213       }
8214     }
8215
8216     if (MovDelay[x][y])         // wait some time before next movement
8217     {
8218       MovDelay[x][y]--;
8219
8220       if (element == EL_ROBOT ||
8221           element == EL_YAMYAM ||
8222           element == EL_DARK_YAMYAM)
8223       {
8224         DrawLevelElementAnimationIfNeeded(x, y, element);
8225         PlayLevelSoundAction(x, y, ACTION_WAITING);
8226       }
8227       else if (element == EL_SP_ELECTRON)
8228         DrawLevelElementAnimationIfNeeded(x, y, element);
8229       else if (element == EL_DRAGON)
8230       {
8231         int i;
8232         int dir = MovDir[x][y];
8233         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8234         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8235         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8236                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8237                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8238                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8239         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8240
8241         GfxAction[x][y] = ACTION_ATTACKING;
8242
8243         if (IS_PLAYER(x, y))
8244           DrawPlayerField(x, y);
8245         else
8246           TEST_DrawLevelField(x, y);
8247
8248         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8249
8250         for (i = 1; i <= 3; i++)
8251         {
8252           int xx = x + i * dx;
8253           int yy = y + i * dy;
8254           int sx = SCREENX(xx);
8255           int sy = SCREENY(yy);
8256           int flame_graphic = graphic + (i - 1);
8257
8258           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8259             break;
8260
8261           if (MovDelay[x][y])
8262           {
8263             int flamed = MovingOrBlocked2Element(xx, yy);
8264
8265             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8266               Bang(xx, yy);
8267             else
8268               RemoveMovingField(xx, yy);
8269
8270             ChangeDelay[xx][yy] = 0;
8271
8272             Tile[xx][yy] = EL_FLAMES;
8273
8274             if (IN_SCR_FIELD(sx, sy))
8275             {
8276               TEST_DrawLevelFieldCrumbled(xx, yy);
8277               DrawGraphic(sx, sy, flame_graphic, frame);
8278             }
8279           }
8280           else
8281           {
8282             if (Tile[xx][yy] == EL_FLAMES)
8283               Tile[xx][yy] = EL_EMPTY;
8284             TEST_DrawLevelField(xx, yy);
8285           }
8286         }
8287       }
8288
8289       if (MovDelay[x][y])       // element still has to wait some time
8290       {
8291         PlayLevelSoundAction(x, y, ACTION_WAITING);
8292
8293         return;
8294       }
8295     }
8296
8297     // now make next step
8298
8299     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8300
8301     if (DONT_COLLIDE_WITH(element) &&
8302         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8303         !PLAYER_ENEMY_PROTECTED(newx, newy))
8304     {
8305       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8306
8307       return;
8308     }
8309
8310     else if (CAN_MOVE_INTO_ACID(element) &&
8311              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8312              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8313              (MovDir[x][y] == MV_DOWN ||
8314               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8315     {
8316       SplashAcid(newx, newy);
8317       Store[x][y] = EL_ACID;
8318     }
8319     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8320     {
8321       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8322           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8323           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8324           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8325       {
8326         RemoveField(x, y);
8327         TEST_DrawLevelField(x, y);
8328
8329         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8330         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8331           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8332
8333         game.friends_still_needed--;
8334         if (!game.friends_still_needed &&
8335             !game.GameOver &&
8336             game.all_players_gone)
8337           LevelSolved();
8338
8339         return;
8340       }
8341       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8342       {
8343         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8344           TEST_DrawLevelField(newx, newy);
8345         else
8346           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8347       }
8348       else if (!IS_FREE(newx, newy))
8349       {
8350         GfxAction[x][y] = ACTION_WAITING;
8351
8352         if (IS_PLAYER(x, y))
8353           DrawPlayerField(x, y);
8354         else
8355           TEST_DrawLevelField(x, y);
8356
8357         return;
8358       }
8359     }
8360     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8361     {
8362       if (IS_FOOD_PIG(Tile[newx][newy]))
8363       {
8364         if (IS_MOVING(newx, newy))
8365           RemoveMovingField(newx, newy);
8366         else
8367         {
8368           Tile[newx][newy] = EL_EMPTY;
8369           TEST_DrawLevelField(newx, newy);
8370         }
8371
8372         PlayLevelSound(x, y, SND_PIG_DIGGING);
8373       }
8374       else if (!IS_FREE(newx, newy))
8375       {
8376         if (IS_PLAYER(x, y))
8377           DrawPlayerField(x, y);
8378         else
8379           TEST_DrawLevelField(x, y);
8380
8381         return;
8382       }
8383     }
8384     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8385     {
8386       if (Store[x][y] != EL_EMPTY)
8387       {
8388         boolean can_clone = FALSE;
8389         int xx, yy;
8390
8391         // check if element to clone is still there
8392         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8393         {
8394           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8395           {
8396             can_clone = TRUE;
8397
8398             break;
8399           }
8400         }
8401
8402         // cannot clone or target field not free anymore -- do not clone
8403         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8404           Store[x][y] = EL_EMPTY;
8405       }
8406
8407       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8408       {
8409         if (IS_MV_DIAGONAL(MovDir[x][y]))
8410         {
8411           int diagonal_move_dir = MovDir[x][y];
8412           int stored = Store[x][y];
8413           int change_delay = 8;
8414           int graphic;
8415
8416           // android is moving diagonally
8417
8418           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8419
8420           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8421           GfxElement[x][y] = EL_EMC_ANDROID;
8422           GfxAction[x][y] = ACTION_SHRINKING;
8423           GfxDir[x][y] = diagonal_move_dir;
8424           ChangeDelay[x][y] = change_delay;
8425
8426           if (Store[x][y] == EL_EMPTY)
8427             Store[x][y] = GfxElementEmpty[x][y];
8428
8429           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8430                                    GfxDir[x][y]);
8431
8432           DrawLevelGraphicAnimation(x, y, graphic);
8433           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8434
8435           if (Tile[newx][newy] == EL_ACID)
8436           {
8437             SplashAcid(newx, newy);
8438
8439             return;
8440           }
8441
8442           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8443
8444           Store[newx][newy] = EL_EMC_ANDROID;
8445           GfxElement[newx][newy] = EL_EMC_ANDROID;
8446           GfxAction[newx][newy] = ACTION_GROWING;
8447           GfxDir[newx][newy] = diagonal_move_dir;
8448           ChangeDelay[newx][newy] = change_delay;
8449
8450           graphic = el_act_dir2img(GfxElement[newx][newy],
8451                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8452
8453           DrawLevelGraphicAnimation(newx, newy, graphic);
8454           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8455
8456           return;
8457         }
8458         else
8459         {
8460           Tile[newx][newy] = EL_EMPTY;
8461           TEST_DrawLevelField(newx, newy);
8462
8463           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8464         }
8465       }
8466       else if (!IS_FREE(newx, newy))
8467       {
8468         return;
8469       }
8470     }
8471     else if (IS_CUSTOM_ELEMENT(element) &&
8472              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8473     {
8474       if (!DigFieldByCE(newx, newy, element))
8475         return;
8476
8477       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8478       {
8479         RunnerVisit[x][y] = FrameCounter;
8480         PlayerVisit[x][y] /= 8;         // expire player visit path
8481       }
8482     }
8483     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8484     {
8485       if (!IS_FREE(newx, newy))
8486       {
8487         if (IS_PLAYER(x, y))
8488           DrawPlayerField(x, y);
8489         else
8490           TEST_DrawLevelField(x, y);
8491
8492         return;
8493       }
8494       else
8495       {
8496         boolean wanna_flame = !RND(10);
8497         int dx = newx - x, dy = newy - y;
8498         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8499         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8500         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8501                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8502         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8503                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8504
8505         if ((wanna_flame ||
8506              IS_CLASSIC_ENEMY(element1) ||
8507              IS_CLASSIC_ENEMY(element2)) &&
8508             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8509             element1 != EL_FLAMES && element2 != EL_FLAMES)
8510         {
8511           ResetGfxAnimation(x, y);
8512           GfxAction[x][y] = ACTION_ATTACKING;
8513
8514           if (IS_PLAYER(x, y))
8515             DrawPlayerField(x, y);
8516           else
8517             TEST_DrawLevelField(x, y);
8518
8519           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8520
8521           MovDelay[x][y] = 50;
8522
8523           Tile[newx][newy] = EL_FLAMES;
8524           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8525             Tile[newx1][newy1] = EL_FLAMES;
8526           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8527             Tile[newx2][newy2] = EL_FLAMES;
8528
8529           return;
8530         }
8531       }
8532     }
8533     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8534              Tile[newx][newy] == EL_DIAMOND)
8535     {
8536       if (IS_MOVING(newx, newy))
8537         RemoveMovingField(newx, newy);
8538       else
8539       {
8540         Tile[newx][newy] = EL_EMPTY;
8541         TEST_DrawLevelField(newx, newy);
8542       }
8543
8544       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8545     }
8546     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8547              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8548     {
8549       if (AmoebaNr[newx][newy])
8550       {
8551         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8552         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8553             Tile[newx][newy] == EL_BD_AMOEBA)
8554           AmoebaCnt[AmoebaNr[newx][newy]]--;
8555       }
8556
8557       if (IS_MOVING(newx, newy))
8558       {
8559         RemoveMovingField(newx, newy);
8560       }
8561       else
8562       {
8563         Tile[newx][newy] = EL_EMPTY;
8564         TEST_DrawLevelField(newx, newy);
8565       }
8566
8567       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8568     }
8569     else if ((element == EL_PACMAN || element == EL_MOLE)
8570              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8571     {
8572       if (AmoebaNr[newx][newy])
8573       {
8574         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8575         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8576             Tile[newx][newy] == EL_BD_AMOEBA)
8577           AmoebaCnt[AmoebaNr[newx][newy]]--;
8578       }
8579
8580       if (element == EL_MOLE)
8581       {
8582         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8583         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8584
8585         ResetGfxAnimation(x, y);
8586         GfxAction[x][y] = ACTION_DIGGING;
8587         TEST_DrawLevelField(x, y);
8588
8589         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8590
8591         return;                         // wait for shrinking amoeba
8592       }
8593       else      // element == EL_PACMAN
8594       {
8595         Tile[newx][newy] = EL_EMPTY;
8596         TEST_DrawLevelField(newx, newy);
8597         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8598       }
8599     }
8600     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8601              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8602               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8603     {
8604       // wait for shrinking amoeba to completely disappear
8605       return;
8606     }
8607     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8608     {
8609       // object was running against a wall
8610
8611       TurnRound(x, y);
8612
8613       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8614         DrawLevelElementAnimation(x, y, element);
8615
8616       if (DONT_TOUCH(element))
8617         TestIfBadThingTouchesPlayer(x, y);
8618
8619       return;
8620     }
8621
8622     InitMovingField(x, y, MovDir[x][y]);
8623
8624     PlayLevelSoundAction(x, y, ACTION_MOVING);
8625   }
8626
8627   if (MovDir[x][y])
8628     ContinueMoving(x, y);
8629 }
8630
8631 void ContinueMoving(int x, int y)
8632 {
8633   int element = Tile[x][y];
8634   struct ElementInfo *ei = &element_info[element];
8635   int direction = MovDir[x][y];
8636   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8637   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8638   int newx = x + dx, newy = y + dy;
8639   int stored = Store[x][y];
8640   int stored_new = Store[newx][newy];
8641   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8642   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8643   boolean last_line = (newy == lev_fieldy - 1);
8644   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8645
8646   if (pushed_by_player)         // special case: moving object pushed by player
8647   {
8648     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8649   }
8650   else if (use_step_delay)      // special case: moving object has step delay
8651   {
8652     if (!MovDelay[x][y])
8653       MovPos[x][y] += getElementMoveStepsize(x, y);
8654
8655     if (MovDelay[x][y])
8656       MovDelay[x][y]--;
8657     else
8658       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8659
8660     if (MovDelay[x][y])
8661     {
8662       TEST_DrawLevelField(x, y);
8663
8664       return;   // element is still waiting
8665     }
8666   }
8667   else                          // normal case: generically moving object
8668   {
8669     MovPos[x][y] += getElementMoveStepsize(x, y);
8670   }
8671
8672   if (ABS(MovPos[x][y]) < TILEX)
8673   {
8674     TEST_DrawLevelField(x, y);
8675
8676     return;     // element is still moving
8677   }
8678
8679   // element reached destination field
8680
8681   Tile[x][y] = EL_EMPTY;
8682   Tile[newx][newy] = element;
8683   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8684
8685   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8686   {
8687     element = Tile[newx][newy] = EL_ACID;
8688   }
8689   else if (element == EL_MOLE)
8690   {
8691     Tile[x][y] = EL_SAND;
8692
8693     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8694   }
8695   else if (element == EL_QUICKSAND_FILLING)
8696   {
8697     element = Tile[newx][newy] = get_next_element(element);
8698     Store[newx][newy] = Store[x][y];
8699   }
8700   else if (element == EL_QUICKSAND_EMPTYING)
8701   {
8702     Tile[x][y] = get_next_element(element);
8703     element = Tile[newx][newy] = Store[x][y];
8704   }
8705   else if (element == EL_QUICKSAND_FAST_FILLING)
8706   {
8707     element = Tile[newx][newy] = get_next_element(element);
8708     Store[newx][newy] = Store[x][y];
8709   }
8710   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8711   {
8712     Tile[x][y] = get_next_element(element);
8713     element = Tile[newx][newy] = Store[x][y];
8714   }
8715   else if (element == EL_MAGIC_WALL_FILLING)
8716   {
8717     element = Tile[newx][newy] = get_next_element(element);
8718     if (!game.magic_wall_active)
8719       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8720     Store[newx][newy] = Store[x][y];
8721   }
8722   else if (element == EL_MAGIC_WALL_EMPTYING)
8723   {
8724     Tile[x][y] = get_next_element(element);
8725     if (!game.magic_wall_active)
8726       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8727     element = Tile[newx][newy] = Store[x][y];
8728
8729     InitField(newx, newy, FALSE);
8730   }
8731   else if (element == EL_BD_MAGIC_WALL_FILLING)
8732   {
8733     element = Tile[newx][newy] = get_next_element(element);
8734     if (!game.magic_wall_active)
8735       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8736     Store[newx][newy] = Store[x][y];
8737   }
8738   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8739   {
8740     Tile[x][y] = get_next_element(element);
8741     if (!game.magic_wall_active)
8742       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8743     element = Tile[newx][newy] = Store[x][y];
8744
8745     InitField(newx, newy, FALSE);
8746   }
8747   else if (element == EL_DC_MAGIC_WALL_FILLING)
8748   {
8749     element = Tile[newx][newy] = get_next_element(element);
8750     if (!game.magic_wall_active)
8751       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8752     Store[newx][newy] = Store[x][y];
8753   }
8754   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8755   {
8756     Tile[x][y] = get_next_element(element);
8757     if (!game.magic_wall_active)
8758       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8759     element = Tile[newx][newy] = Store[x][y];
8760
8761     InitField(newx, newy, FALSE);
8762   }
8763   else if (element == EL_AMOEBA_DROPPING)
8764   {
8765     Tile[x][y] = get_next_element(element);
8766     element = Tile[newx][newy] = Store[x][y];
8767   }
8768   else if (element == EL_SOKOBAN_OBJECT)
8769   {
8770     if (Back[x][y])
8771       Tile[x][y] = Back[x][y];
8772
8773     if (Back[newx][newy])
8774       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8775
8776     Back[x][y] = Back[newx][newy] = 0;
8777   }
8778
8779   Store[x][y] = EL_EMPTY;
8780   MovPos[x][y] = 0;
8781   MovDir[x][y] = 0;
8782   MovDelay[x][y] = 0;
8783
8784   MovDelay[newx][newy] = 0;
8785
8786   if (CAN_CHANGE_OR_HAS_ACTION(element))
8787   {
8788     // copy element change control values to new field
8789     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8790     ChangePage[newx][newy]  = ChangePage[x][y];
8791     ChangeCount[newx][newy] = ChangeCount[x][y];
8792     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8793   }
8794
8795   CustomValue[newx][newy] = CustomValue[x][y];
8796
8797   ChangeDelay[x][y] = 0;
8798   ChangePage[x][y] = -1;
8799   ChangeCount[x][y] = 0;
8800   ChangeEvent[x][y] = -1;
8801
8802   CustomValue[x][y] = 0;
8803
8804   // copy animation control values to new field
8805   GfxFrame[newx][newy]  = GfxFrame[x][y];
8806   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8807   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8808   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8809
8810   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8811
8812   // some elements can leave other elements behind after moving
8813   if (ei->move_leave_element != EL_EMPTY &&
8814       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8815       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8816   {
8817     int move_leave_element = ei->move_leave_element;
8818
8819     // this makes it possible to leave the removed element again
8820     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8821       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8822
8823     Tile[x][y] = move_leave_element;
8824
8825     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8826       MovDir[x][y] = direction;
8827
8828     InitField(x, y, FALSE);
8829
8830     if (GFX_CRUMBLED(Tile[x][y]))
8831       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8832
8833     if (IS_PLAYER_ELEMENT(move_leave_element))
8834       RelocatePlayer(x, y, move_leave_element);
8835   }
8836
8837   // do this after checking for left-behind element
8838   ResetGfxAnimation(x, y);      // reset animation values for old field
8839
8840   if (!CAN_MOVE(element) ||
8841       (CAN_FALL(element) && direction == MV_DOWN &&
8842        (element == EL_SPRING ||
8843         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8844         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8845     GfxDir[x][y] = MovDir[newx][newy] = 0;
8846
8847   TEST_DrawLevelField(x, y);
8848   TEST_DrawLevelField(newx, newy);
8849
8850   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8851
8852   // prevent pushed element from moving on in pushed direction
8853   if (pushed_by_player && CAN_MOVE(element) &&
8854       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8855       !(element_info[element].move_pattern & direction))
8856     TurnRound(newx, newy);
8857
8858   // prevent elements on conveyor belt from moving on in last direction
8859   if (pushed_by_conveyor && CAN_FALL(element) &&
8860       direction & MV_HORIZONTAL)
8861     MovDir[newx][newy] = 0;
8862
8863   if (!pushed_by_player)
8864   {
8865     int nextx = newx + dx, nexty = newy + dy;
8866     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8867
8868     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8869
8870     if (CAN_FALL(element) && direction == MV_DOWN)
8871       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8872
8873     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8874       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8875
8876     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8877       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8878   }
8879
8880   if (DONT_TOUCH(element))      // object may be nasty to player or others
8881   {
8882     TestIfBadThingTouchesPlayer(newx, newy);
8883     TestIfBadThingTouchesFriend(newx, newy);
8884
8885     if (!IS_CUSTOM_ELEMENT(element))
8886       TestIfBadThingTouchesOtherBadThing(newx, newy);
8887   }
8888   else if (element == EL_PENGUIN)
8889     TestIfFriendTouchesBadThing(newx, newy);
8890
8891   if (DONT_GET_HIT_BY(element))
8892   {
8893     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8894   }
8895
8896   // give the player one last chance (one more frame) to move away
8897   if (CAN_FALL(element) && direction == MV_DOWN &&
8898       (last_line || (!IS_FREE(x, newy + 1) &&
8899                      (!IS_PLAYER(x, newy + 1) ||
8900                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8901     Impact(x, newy);
8902
8903   if (pushed_by_player && !game.use_change_when_pushing_bug)
8904   {
8905     int push_side = MV_DIR_OPPOSITE(direction);
8906     struct PlayerInfo *player = PLAYERINFO(x, y);
8907
8908     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8909                                player->index_bit, push_side);
8910     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8911                                         player->index_bit, push_side);
8912   }
8913
8914   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8915     MovDelay[newx][newy] = 1;
8916
8917   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8918
8919   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8920   TestIfElementHitsCustomElement(newx, newy, direction);
8921   TestIfPlayerTouchesCustomElement(newx, newy);
8922   TestIfElementTouchesCustomElement(newx, newy);
8923
8924   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8925       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8926     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8927                              MV_DIR_OPPOSITE(direction));
8928 }
8929
8930 int AmoebaNeighbourNr(int ax, int ay)
8931 {
8932   int i;
8933   int element = Tile[ax][ay];
8934   int group_nr = 0;
8935   static int xy[4][2] =
8936   {
8937     { 0, -1 },
8938     { -1, 0 },
8939     { +1, 0 },
8940     { 0, +1 }
8941   };
8942
8943   for (i = 0; i < NUM_DIRECTIONS; i++)
8944   {
8945     int x = ax + xy[i][0];
8946     int y = ay + xy[i][1];
8947
8948     if (!IN_LEV_FIELD(x, y))
8949       continue;
8950
8951     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8952       group_nr = AmoebaNr[x][y];
8953   }
8954
8955   return group_nr;
8956 }
8957
8958 static void AmoebaMerge(int ax, int ay)
8959 {
8960   int i, x, y, xx, yy;
8961   int new_group_nr = AmoebaNr[ax][ay];
8962   static int xy[4][2] =
8963   {
8964     { 0, -1 },
8965     { -1, 0 },
8966     { +1, 0 },
8967     { 0, +1 }
8968   };
8969
8970   if (new_group_nr == 0)
8971     return;
8972
8973   for (i = 0; i < NUM_DIRECTIONS; i++)
8974   {
8975     x = ax + xy[i][0];
8976     y = ay + xy[i][1];
8977
8978     if (!IN_LEV_FIELD(x, y))
8979       continue;
8980
8981     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8982          Tile[x][y] == EL_BD_AMOEBA ||
8983          Tile[x][y] == EL_AMOEBA_DEAD) &&
8984         AmoebaNr[x][y] != new_group_nr)
8985     {
8986       int old_group_nr = AmoebaNr[x][y];
8987
8988       if (old_group_nr == 0)
8989         return;
8990
8991       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8992       AmoebaCnt[old_group_nr] = 0;
8993       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8994       AmoebaCnt2[old_group_nr] = 0;
8995
8996       SCAN_PLAYFIELD(xx, yy)
8997       {
8998         if (AmoebaNr[xx][yy] == old_group_nr)
8999           AmoebaNr[xx][yy] = new_group_nr;
9000       }
9001     }
9002   }
9003 }
9004
9005 void AmoebaToDiamond(int ax, int ay)
9006 {
9007   int i, x, y;
9008
9009   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9010   {
9011     int group_nr = AmoebaNr[ax][ay];
9012
9013 #ifdef DEBUG
9014     if (group_nr == 0)
9015     {
9016       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9017       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9018
9019       return;
9020     }
9021 #endif
9022
9023     SCAN_PLAYFIELD(x, y)
9024     {
9025       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9026       {
9027         AmoebaNr[x][y] = 0;
9028         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9029       }
9030     }
9031
9032     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9033                             SND_AMOEBA_TURNING_TO_GEM :
9034                             SND_AMOEBA_TURNING_TO_ROCK));
9035     Bang(ax, ay);
9036   }
9037   else
9038   {
9039     static int xy[4][2] =
9040     {
9041       { 0, -1 },
9042       { -1, 0 },
9043       { +1, 0 },
9044       { 0, +1 }
9045     };
9046
9047     for (i = 0; i < NUM_DIRECTIONS; i++)
9048     {
9049       x = ax + xy[i][0];
9050       y = ay + xy[i][1];
9051
9052       if (!IN_LEV_FIELD(x, y))
9053         continue;
9054
9055       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9056       {
9057         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9058                               SND_AMOEBA_TURNING_TO_GEM :
9059                               SND_AMOEBA_TURNING_TO_ROCK));
9060         Bang(x, y);
9061       }
9062     }
9063   }
9064 }
9065
9066 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9067 {
9068   int x, y;
9069   int group_nr = AmoebaNr[ax][ay];
9070   boolean done = FALSE;
9071
9072 #ifdef DEBUG
9073   if (group_nr == 0)
9074   {
9075     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9076     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9077
9078     return;
9079   }
9080 #endif
9081
9082   SCAN_PLAYFIELD(x, y)
9083   {
9084     if (AmoebaNr[x][y] == group_nr &&
9085         (Tile[x][y] == EL_AMOEBA_DEAD ||
9086          Tile[x][y] == EL_BD_AMOEBA ||
9087          Tile[x][y] == EL_AMOEBA_GROWING))
9088     {
9089       AmoebaNr[x][y] = 0;
9090       Tile[x][y] = new_element;
9091       InitField(x, y, FALSE);
9092       TEST_DrawLevelField(x, y);
9093       done = TRUE;
9094     }
9095   }
9096
9097   if (done)
9098     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9099                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9100                             SND_BD_AMOEBA_TURNING_TO_GEM));
9101 }
9102
9103 static void AmoebaGrowing(int x, int y)
9104 {
9105   static unsigned int sound_delay = 0;
9106   static unsigned int sound_delay_value = 0;
9107
9108   if (!MovDelay[x][y])          // start new growing cycle
9109   {
9110     MovDelay[x][y] = 7;
9111
9112     if (DelayReached(&sound_delay, sound_delay_value))
9113     {
9114       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9115       sound_delay_value = 30;
9116     }
9117   }
9118
9119   if (MovDelay[x][y])           // wait some time before growing bigger
9120   {
9121     MovDelay[x][y]--;
9122     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9123     {
9124       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9125                                            6 - MovDelay[x][y]);
9126
9127       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9128     }
9129
9130     if (!MovDelay[x][y])
9131     {
9132       Tile[x][y] = Store[x][y];
9133       Store[x][y] = 0;
9134       TEST_DrawLevelField(x, y);
9135     }
9136   }
9137 }
9138
9139 static void AmoebaShrinking(int x, int y)
9140 {
9141   static unsigned int sound_delay = 0;
9142   static unsigned int sound_delay_value = 0;
9143
9144   if (!MovDelay[x][y])          // start new shrinking cycle
9145   {
9146     MovDelay[x][y] = 7;
9147
9148     if (DelayReached(&sound_delay, sound_delay_value))
9149       sound_delay_value = 30;
9150   }
9151
9152   if (MovDelay[x][y])           // wait some time before shrinking
9153   {
9154     MovDelay[x][y]--;
9155     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9156     {
9157       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9158                                            6 - MovDelay[x][y]);
9159
9160       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9161     }
9162
9163     if (!MovDelay[x][y])
9164     {
9165       Tile[x][y] = EL_EMPTY;
9166       TEST_DrawLevelField(x, y);
9167
9168       // don't let mole enter this field in this cycle;
9169       // (give priority to objects falling to this field from above)
9170       Stop[x][y] = TRUE;
9171     }
9172   }
9173 }
9174
9175 static void AmoebaReproduce(int ax, int ay)
9176 {
9177   int i;
9178   int element = Tile[ax][ay];
9179   int graphic = el2img(element);
9180   int newax = ax, neway = ay;
9181   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9182   static int xy[4][2] =
9183   {
9184     { 0, -1 },
9185     { -1, 0 },
9186     { +1, 0 },
9187     { 0, +1 }
9188   };
9189
9190   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9191   {
9192     Tile[ax][ay] = EL_AMOEBA_DEAD;
9193     TEST_DrawLevelField(ax, ay);
9194     return;
9195   }
9196
9197   if (IS_ANIMATED(graphic))
9198     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9199
9200   if (!MovDelay[ax][ay])        // start making new amoeba field
9201     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9202
9203   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9204   {
9205     MovDelay[ax][ay]--;
9206     if (MovDelay[ax][ay])
9207       return;
9208   }
9209
9210   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9211   {
9212     int start = RND(4);
9213     int x = ax + xy[start][0];
9214     int y = ay + xy[start][1];
9215
9216     if (!IN_LEV_FIELD(x, y))
9217       return;
9218
9219     if (IS_FREE(x, y) ||
9220         CAN_GROW_INTO(Tile[x][y]) ||
9221         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9222         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9223     {
9224       newax = x;
9225       neway = y;
9226     }
9227
9228     if (newax == ax && neway == ay)
9229       return;
9230   }
9231   else                          // normal or "filled" (BD style) amoeba
9232   {
9233     int start = RND(4);
9234     boolean waiting_for_player = FALSE;
9235
9236     for (i = 0; i < NUM_DIRECTIONS; i++)
9237     {
9238       int j = (start + i) % 4;
9239       int x = ax + xy[j][0];
9240       int y = ay + xy[j][1];
9241
9242       if (!IN_LEV_FIELD(x, y))
9243         continue;
9244
9245       if (IS_FREE(x, y) ||
9246           CAN_GROW_INTO(Tile[x][y]) ||
9247           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9248           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9249       {
9250         newax = x;
9251         neway = y;
9252         break;
9253       }
9254       else if (IS_PLAYER(x, y))
9255         waiting_for_player = TRUE;
9256     }
9257
9258     if (newax == ax && neway == ay)             // amoeba cannot grow
9259     {
9260       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9261       {
9262         Tile[ax][ay] = EL_AMOEBA_DEAD;
9263         TEST_DrawLevelField(ax, ay);
9264         AmoebaCnt[AmoebaNr[ax][ay]]--;
9265
9266         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9267         {
9268           if (element == EL_AMOEBA_FULL)
9269             AmoebaToDiamond(ax, ay);
9270           else if (element == EL_BD_AMOEBA)
9271             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9272         }
9273       }
9274       return;
9275     }
9276     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9277     {
9278       // amoeba gets larger by growing in some direction
9279
9280       int new_group_nr = AmoebaNr[ax][ay];
9281
9282 #ifdef DEBUG
9283   if (new_group_nr == 0)
9284   {
9285     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9286           newax, neway);
9287     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9288
9289     return;
9290   }
9291 #endif
9292
9293       AmoebaNr[newax][neway] = new_group_nr;
9294       AmoebaCnt[new_group_nr]++;
9295       AmoebaCnt2[new_group_nr]++;
9296
9297       // if amoeba touches other amoeba(s) after growing, unify them
9298       AmoebaMerge(newax, neway);
9299
9300       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9301       {
9302         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9303         return;
9304       }
9305     }
9306   }
9307
9308   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9309       (neway == lev_fieldy - 1 && newax != ax))
9310   {
9311     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9312     Store[newax][neway] = element;
9313   }
9314   else if (neway == ay || element == EL_EMC_DRIPPER)
9315   {
9316     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9317
9318     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9319   }
9320   else
9321   {
9322     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9323     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9324     Store[ax][ay] = EL_AMOEBA_DROP;
9325     ContinueMoving(ax, ay);
9326     return;
9327   }
9328
9329   TEST_DrawLevelField(newax, neway);
9330 }
9331
9332 static void Life(int ax, int ay)
9333 {
9334   int x1, y1, x2, y2;
9335   int life_time = 40;
9336   int element = Tile[ax][ay];
9337   int graphic = el2img(element);
9338   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9339                          level.biomaze);
9340   boolean changed = FALSE;
9341
9342   if (IS_ANIMATED(graphic))
9343     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9344
9345   if (Stop[ax][ay])
9346     return;
9347
9348   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9349     MovDelay[ax][ay] = life_time;
9350
9351   if (MovDelay[ax][ay])         // wait some time before next cycle
9352   {
9353     MovDelay[ax][ay]--;
9354     if (MovDelay[ax][ay])
9355       return;
9356   }
9357
9358   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9359   {
9360     int xx = ax+x1, yy = ay+y1;
9361     int old_element = Tile[xx][yy];
9362     int num_neighbours = 0;
9363
9364     if (!IN_LEV_FIELD(xx, yy))
9365       continue;
9366
9367     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9368     {
9369       int x = xx+x2, y = yy+y2;
9370
9371       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9372         continue;
9373
9374       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9375       boolean is_neighbour = FALSE;
9376
9377       if (level.use_life_bugs)
9378         is_neighbour =
9379           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9380            (IS_FREE(x, y)                             &&  Stop[x][y]));
9381       else
9382         is_neighbour =
9383           (Last[x][y] == element || is_player_cell);
9384
9385       if (is_neighbour)
9386         num_neighbours++;
9387     }
9388
9389     boolean is_free = FALSE;
9390
9391     if (level.use_life_bugs)
9392       is_free = (IS_FREE(xx, yy));
9393     else
9394       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9395
9396     if (xx == ax && yy == ay)           // field in the middle
9397     {
9398       if (num_neighbours < life_parameter[0] ||
9399           num_neighbours > life_parameter[1])
9400       {
9401         Tile[xx][yy] = EL_EMPTY;
9402         if (Tile[xx][yy] != old_element)
9403           TEST_DrawLevelField(xx, yy);
9404         Stop[xx][yy] = TRUE;
9405         changed = TRUE;
9406       }
9407     }
9408     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9409     {                                   // free border field
9410       if (num_neighbours >= life_parameter[2] &&
9411           num_neighbours <= life_parameter[3])
9412       {
9413         Tile[xx][yy] = element;
9414         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9415         if (Tile[xx][yy] != old_element)
9416           TEST_DrawLevelField(xx, yy);
9417         Stop[xx][yy] = TRUE;
9418         changed = TRUE;
9419       }
9420     }
9421   }
9422
9423   if (changed)
9424     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9425                    SND_GAME_OF_LIFE_GROWING);
9426 }
9427
9428 static void InitRobotWheel(int x, int y)
9429 {
9430   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9431 }
9432
9433 static void RunRobotWheel(int x, int y)
9434 {
9435   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9436 }
9437
9438 static void StopRobotWheel(int x, int y)
9439 {
9440   if (game.robot_wheel_x == x &&
9441       game.robot_wheel_y == y)
9442   {
9443     game.robot_wheel_x = -1;
9444     game.robot_wheel_y = -1;
9445     game.robot_wheel_active = FALSE;
9446   }
9447 }
9448
9449 static void InitTimegateWheel(int x, int y)
9450 {
9451   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9452 }
9453
9454 static void RunTimegateWheel(int x, int y)
9455 {
9456   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9457 }
9458
9459 static void InitMagicBallDelay(int x, int y)
9460 {
9461   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9462 }
9463
9464 static void ActivateMagicBall(int bx, int by)
9465 {
9466   int x, y;
9467
9468   if (level.ball_random)
9469   {
9470     int pos_border = RND(8);    // select one of the eight border elements
9471     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9472     int xx = pos_content % 3;
9473     int yy = pos_content / 3;
9474
9475     x = bx - 1 + xx;
9476     y = by - 1 + yy;
9477
9478     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9479       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9480   }
9481   else
9482   {
9483     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9484     {
9485       int xx = x - bx + 1;
9486       int yy = y - by + 1;
9487
9488       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9489         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9490     }
9491   }
9492
9493   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9494 }
9495
9496 static void CheckExit(int x, int y)
9497 {
9498   if (game.gems_still_needed > 0 ||
9499       game.sokoban_fields_still_needed > 0 ||
9500       game.sokoban_objects_still_needed > 0 ||
9501       game.lights_still_needed > 0)
9502   {
9503     int element = Tile[x][y];
9504     int graphic = el2img(element);
9505
9506     if (IS_ANIMATED(graphic))
9507       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9508
9509     return;
9510   }
9511
9512   // do not re-open exit door closed after last player
9513   if (game.all_players_gone)
9514     return;
9515
9516   Tile[x][y] = EL_EXIT_OPENING;
9517
9518   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9519 }
9520
9521 static void CheckExitEM(int x, int y)
9522 {
9523   if (game.gems_still_needed > 0 ||
9524       game.sokoban_fields_still_needed > 0 ||
9525       game.sokoban_objects_still_needed > 0 ||
9526       game.lights_still_needed > 0)
9527   {
9528     int element = Tile[x][y];
9529     int graphic = el2img(element);
9530
9531     if (IS_ANIMATED(graphic))
9532       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9533
9534     return;
9535   }
9536
9537   // do not re-open exit door closed after last player
9538   if (game.all_players_gone)
9539     return;
9540
9541   Tile[x][y] = EL_EM_EXIT_OPENING;
9542
9543   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9544 }
9545
9546 static void CheckExitSteel(int x, int y)
9547 {
9548   if (game.gems_still_needed > 0 ||
9549       game.sokoban_fields_still_needed > 0 ||
9550       game.sokoban_objects_still_needed > 0 ||
9551       game.lights_still_needed > 0)
9552   {
9553     int element = Tile[x][y];
9554     int graphic = el2img(element);
9555
9556     if (IS_ANIMATED(graphic))
9557       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9558
9559     return;
9560   }
9561
9562   // do not re-open exit door closed after last player
9563   if (game.all_players_gone)
9564     return;
9565
9566   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9567
9568   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9569 }
9570
9571 static void CheckExitSteelEM(int x, int y)
9572 {
9573   if (game.gems_still_needed > 0 ||
9574       game.sokoban_fields_still_needed > 0 ||
9575       game.sokoban_objects_still_needed > 0 ||
9576       game.lights_still_needed > 0)
9577   {
9578     int element = Tile[x][y];
9579     int graphic = el2img(element);
9580
9581     if (IS_ANIMATED(graphic))
9582       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9583
9584     return;
9585   }
9586
9587   // do not re-open exit door closed after last player
9588   if (game.all_players_gone)
9589     return;
9590
9591   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9592
9593   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9594 }
9595
9596 static void CheckExitSP(int x, int y)
9597 {
9598   if (game.gems_still_needed > 0)
9599   {
9600     int element = Tile[x][y];
9601     int graphic = el2img(element);
9602
9603     if (IS_ANIMATED(graphic))
9604       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9605
9606     return;
9607   }
9608
9609   // do not re-open exit door closed after last player
9610   if (game.all_players_gone)
9611     return;
9612
9613   Tile[x][y] = EL_SP_EXIT_OPENING;
9614
9615   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9616 }
9617
9618 static void CloseAllOpenTimegates(void)
9619 {
9620   int x, y;
9621
9622   SCAN_PLAYFIELD(x, y)
9623   {
9624     int element = Tile[x][y];
9625
9626     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9627     {
9628       Tile[x][y] = EL_TIMEGATE_CLOSING;
9629
9630       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9631     }
9632   }
9633 }
9634
9635 static void DrawTwinkleOnField(int x, int y)
9636 {
9637   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9638     return;
9639
9640   if (Tile[x][y] == EL_BD_DIAMOND)
9641     return;
9642
9643   if (MovDelay[x][y] == 0)      // next animation frame
9644     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9645
9646   if (MovDelay[x][y] != 0)      // wait some time before next frame
9647   {
9648     MovDelay[x][y]--;
9649
9650     DrawLevelElementAnimation(x, y, Tile[x][y]);
9651
9652     if (MovDelay[x][y] != 0)
9653     {
9654       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9655                                            10 - MovDelay[x][y]);
9656
9657       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9658     }
9659   }
9660 }
9661
9662 static void MauerWaechst(int x, int y)
9663 {
9664   int delay = 6;
9665
9666   if (!MovDelay[x][y])          // next animation frame
9667     MovDelay[x][y] = 3 * delay;
9668
9669   if (MovDelay[x][y])           // wait some time before next frame
9670   {
9671     MovDelay[x][y]--;
9672
9673     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9674     {
9675       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9676       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9677
9678       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9679     }
9680
9681     if (!MovDelay[x][y])
9682     {
9683       if (MovDir[x][y] == MV_LEFT)
9684       {
9685         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9686           TEST_DrawLevelField(x - 1, y);
9687       }
9688       else if (MovDir[x][y] == MV_RIGHT)
9689       {
9690         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9691           TEST_DrawLevelField(x + 1, y);
9692       }
9693       else if (MovDir[x][y] == MV_UP)
9694       {
9695         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9696           TEST_DrawLevelField(x, y - 1);
9697       }
9698       else
9699       {
9700         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9701           TEST_DrawLevelField(x, y + 1);
9702       }
9703
9704       Tile[x][y] = Store[x][y];
9705       Store[x][y] = 0;
9706       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9707       TEST_DrawLevelField(x, y);
9708     }
9709   }
9710 }
9711
9712 static void MauerAbleger(int ax, int ay)
9713 {
9714   int element = Tile[ax][ay];
9715   int graphic = el2img(element);
9716   boolean oben_frei = FALSE, unten_frei = FALSE;
9717   boolean links_frei = FALSE, rechts_frei = FALSE;
9718   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9719   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9720   boolean new_wall = FALSE;
9721
9722   if (IS_ANIMATED(graphic))
9723     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9724
9725   if (!MovDelay[ax][ay])        // start building new wall
9726     MovDelay[ax][ay] = 6;
9727
9728   if (MovDelay[ax][ay])         // wait some time before building new wall
9729   {
9730     MovDelay[ax][ay]--;
9731     if (MovDelay[ax][ay])
9732       return;
9733   }
9734
9735   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9736     oben_frei = TRUE;
9737   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9738     unten_frei = TRUE;
9739   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9740     links_frei = TRUE;
9741   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9742     rechts_frei = TRUE;
9743
9744   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9745       element == EL_EXPANDABLE_WALL_ANY)
9746   {
9747     if (oben_frei)
9748     {
9749       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9750       Store[ax][ay-1] = element;
9751       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9752       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9753         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9754                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9755       new_wall = TRUE;
9756     }
9757     if (unten_frei)
9758     {
9759       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9760       Store[ax][ay+1] = element;
9761       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9762       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9763         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9764                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9765       new_wall = TRUE;
9766     }
9767   }
9768
9769   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9770       element == EL_EXPANDABLE_WALL_ANY ||
9771       element == EL_EXPANDABLE_WALL ||
9772       element == EL_BD_EXPANDABLE_WALL)
9773   {
9774     if (links_frei)
9775     {
9776       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9777       Store[ax-1][ay] = element;
9778       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9779       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9780         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9781                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9782       new_wall = TRUE;
9783     }
9784
9785     if (rechts_frei)
9786     {
9787       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9788       Store[ax+1][ay] = element;
9789       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9790       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9791         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9792                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9793       new_wall = TRUE;
9794     }
9795   }
9796
9797   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9798     TEST_DrawLevelField(ax, ay);
9799
9800   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9801     oben_massiv = TRUE;
9802   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9803     unten_massiv = TRUE;
9804   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9805     links_massiv = TRUE;
9806   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9807     rechts_massiv = TRUE;
9808
9809   if (((oben_massiv && unten_massiv) ||
9810        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9811        element == EL_EXPANDABLE_WALL) &&
9812       ((links_massiv && rechts_massiv) ||
9813        element == EL_EXPANDABLE_WALL_VERTICAL))
9814     Tile[ax][ay] = EL_WALL;
9815
9816   if (new_wall)
9817     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9818 }
9819
9820 static void MauerAblegerStahl(int ax, int ay)
9821 {
9822   int element = Tile[ax][ay];
9823   int graphic = el2img(element);
9824   boolean oben_frei = FALSE, unten_frei = FALSE;
9825   boolean links_frei = FALSE, rechts_frei = FALSE;
9826   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9827   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9828   boolean new_wall = FALSE;
9829
9830   if (IS_ANIMATED(graphic))
9831     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9832
9833   if (!MovDelay[ax][ay])        // start building new wall
9834     MovDelay[ax][ay] = 6;
9835
9836   if (MovDelay[ax][ay])         // wait some time before building new wall
9837   {
9838     MovDelay[ax][ay]--;
9839     if (MovDelay[ax][ay])
9840       return;
9841   }
9842
9843   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9844     oben_frei = TRUE;
9845   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9846     unten_frei = TRUE;
9847   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9848     links_frei = TRUE;
9849   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9850     rechts_frei = TRUE;
9851
9852   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9853       element == EL_EXPANDABLE_STEELWALL_ANY)
9854   {
9855     if (oben_frei)
9856     {
9857       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9858       Store[ax][ay-1] = element;
9859       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9860       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9861         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9862                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9863       new_wall = TRUE;
9864     }
9865     if (unten_frei)
9866     {
9867       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9868       Store[ax][ay+1] = element;
9869       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9870       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9871         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9872                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9873       new_wall = TRUE;
9874     }
9875   }
9876
9877   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9878       element == EL_EXPANDABLE_STEELWALL_ANY)
9879   {
9880     if (links_frei)
9881     {
9882       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9883       Store[ax-1][ay] = element;
9884       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9885       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9886         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9887                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9888       new_wall = TRUE;
9889     }
9890
9891     if (rechts_frei)
9892     {
9893       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9894       Store[ax+1][ay] = element;
9895       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9896       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9897         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9898                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9899       new_wall = TRUE;
9900     }
9901   }
9902
9903   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9904     oben_massiv = TRUE;
9905   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9906     unten_massiv = TRUE;
9907   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9908     links_massiv = TRUE;
9909   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9910     rechts_massiv = TRUE;
9911
9912   if (((oben_massiv && unten_massiv) ||
9913        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9914       ((links_massiv && rechts_massiv) ||
9915        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9916     Tile[ax][ay] = EL_STEELWALL;
9917
9918   if (new_wall)
9919     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9920 }
9921
9922 static void CheckForDragon(int x, int y)
9923 {
9924   int i, j;
9925   boolean dragon_found = FALSE;
9926   static int xy[4][2] =
9927   {
9928     { 0, -1 },
9929     { -1, 0 },
9930     { +1, 0 },
9931     { 0, +1 }
9932   };
9933
9934   for (i = 0; i < NUM_DIRECTIONS; i++)
9935   {
9936     for (j = 0; j < 4; j++)
9937     {
9938       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9939
9940       if (IN_LEV_FIELD(xx, yy) &&
9941           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9942       {
9943         if (Tile[xx][yy] == EL_DRAGON)
9944           dragon_found = TRUE;
9945       }
9946       else
9947         break;
9948     }
9949   }
9950
9951   if (!dragon_found)
9952   {
9953     for (i = 0; i < NUM_DIRECTIONS; i++)
9954     {
9955       for (j = 0; j < 3; j++)
9956       {
9957         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9958   
9959         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9960         {
9961           Tile[xx][yy] = EL_EMPTY;
9962           TEST_DrawLevelField(xx, yy);
9963         }
9964         else
9965           break;
9966       }
9967     }
9968   }
9969 }
9970
9971 static void InitBuggyBase(int x, int y)
9972 {
9973   int element = Tile[x][y];
9974   int activating_delay = FRAMES_PER_SECOND / 4;
9975
9976   ChangeDelay[x][y] =
9977     (element == EL_SP_BUGGY_BASE ?
9978      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9979      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9980      activating_delay :
9981      element == EL_SP_BUGGY_BASE_ACTIVE ?
9982      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9983 }
9984
9985 static void WarnBuggyBase(int x, int y)
9986 {
9987   int i;
9988   static int xy[4][2] =
9989   {
9990     { 0, -1 },
9991     { -1, 0 },
9992     { +1, 0 },
9993     { 0, +1 }
9994   };
9995
9996   for (i = 0; i < NUM_DIRECTIONS; i++)
9997   {
9998     int xx = x + xy[i][0];
9999     int yy = y + xy[i][1];
10000
10001     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10002     {
10003       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10004
10005       break;
10006     }
10007   }
10008 }
10009
10010 static void InitTrap(int x, int y)
10011 {
10012   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10013 }
10014
10015 static void ActivateTrap(int x, int y)
10016 {
10017   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10018 }
10019
10020 static void ChangeActiveTrap(int x, int y)
10021 {
10022   int graphic = IMG_TRAP_ACTIVE;
10023
10024   // if new animation frame was drawn, correct crumbled sand border
10025   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10026     TEST_DrawLevelFieldCrumbled(x, y);
10027 }
10028
10029 static int getSpecialActionElement(int element, int number, int base_element)
10030 {
10031   return (element != EL_EMPTY ? element :
10032           number != -1 ? base_element + number - 1 :
10033           EL_EMPTY);
10034 }
10035
10036 static int getModifiedActionNumber(int value_old, int operator, int operand,
10037                                    int value_min, int value_max)
10038 {
10039   int value_new = (operator == CA_MODE_SET      ? operand :
10040                    operator == CA_MODE_ADD      ? value_old + operand :
10041                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10042                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10043                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10044                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10045                    value_old);
10046
10047   return (value_new < value_min ? value_min :
10048           value_new > value_max ? value_max :
10049           value_new);
10050 }
10051
10052 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10053 {
10054   struct ElementInfo *ei = &element_info[element];
10055   struct ElementChangeInfo *change = &ei->change_page[page];
10056   int target_element = change->target_element;
10057   int action_type = change->action_type;
10058   int action_mode = change->action_mode;
10059   int action_arg = change->action_arg;
10060   int action_element = change->action_element;
10061   int i;
10062
10063   if (!change->has_action)
10064     return;
10065
10066   // ---------- determine action paramater values -----------------------------
10067
10068   int level_time_value =
10069     (level.time > 0 ? TimeLeft :
10070      TimePlayed);
10071
10072   int action_arg_element_raw =
10073     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10074      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10075      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10076      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10077      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10078      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10079      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10080      EL_EMPTY);
10081   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10082
10083   int action_arg_direction =
10084     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10085      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10086      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10087      change->actual_trigger_side :
10088      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10089      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10090      MV_NONE);
10091
10092   int action_arg_number_min =
10093     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10094      CA_ARG_MIN);
10095
10096   int action_arg_number_max =
10097     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10098      action_type == CA_SET_LEVEL_GEMS ? 999 :
10099      action_type == CA_SET_LEVEL_TIME ? 9999 :
10100      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10101      action_type == CA_SET_CE_VALUE ? 9999 :
10102      action_type == CA_SET_CE_SCORE ? 9999 :
10103      CA_ARG_MAX);
10104
10105   int action_arg_number_reset =
10106     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10107      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10108      action_type == CA_SET_LEVEL_TIME ? level.time :
10109      action_type == CA_SET_LEVEL_SCORE ? 0 :
10110      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10111      action_type == CA_SET_CE_SCORE ? 0 :
10112      0);
10113
10114   int action_arg_number =
10115     (action_arg <= CA_ARG_MAX ? action_arg :
10116      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10117      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10118      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10119      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10120      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10121      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10122      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10123      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10124      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10125      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10126      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10127      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10128      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10129      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10130      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10131      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10132      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10133      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10134      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10135      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10136      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10137      -1);
10138
10139   int action_arg_number_old =
10140     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10141      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10142      action_type == CA_SET_LEVEL_SCORE ? game.score :
10143      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10144      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10145      0);
10146
10147   int action_arg_number_new =
10148     getModifiedActionNumber(action_arg_number_old,
10149                             action_mode, action_arg_number,
10150                             action_arg_number_min, action_arg_number_max);
10151
10152   int trigger_player_bits =
10153     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10154      change->actual_trigger_player_bits : change->trigger_player);
10155
10156   int action_arg_player_bits =
10157     (action_arg >= CA_ARG_PLAYER_1 &&
10158      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10159      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10160      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10161      PLAYER_BITS_ANY);
10162
10163   // ---------- execute action  -----------------------------------------------
10164
10165   switch (action_type)
10166   {
10167     case CA_NO_ACTION:
10168     {
10169       return;
10170     }
10171
10172     // ---------- level actions  ----------------------------------------------
10173
10174     case CA_RESTART_LEVEL:
10175     {
10176       game.restart_level = TRUE;
10177
10178       break;
10179     }
10180
10181     case CA_SHOW_ENVELOPE:
10182     {
10183       int element = getSpecialActionElement(action_arg_element,
10184                                             action_arg_number, EL_ENVELOPE_1);
10185
10186       if (IS_ENVELOPE(element))
10187         local_player->show_envelope = element;
10188
10189       break;
10190     }
10191
10192     case CA_SET_LEVEL_TIME:
10193     {
10194       if (level.time > 0)       // only modify limited time value
10195       {
10196         TimeLeft = action_arg_number_new;
10197
10198         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10199
10200         DisplayGameControlValues();
10201
10202         if (!TimeLeft && setup.time_limit)
10203           for (i = 0; i < MAX_PLAYERS; i++)
10204             KillPlayer(&stored_player[i]);
10205       }
10206
10207       break;
10208     }
10209
10210     case CA_SET_LEVEL_SCORE:
10211     {
10212       game.score = action_arg_number_new;
10213
10214       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10215
10216       DisplayGameControlValues();
10217
10218       break;
10219     }
10220
10221     case CA_SET_LEVEL_GEMS:
10222     {
10223       game.gems_still_needed = action_arg_number_new;
10224
10225       game.snapshot.collected_item = TRUE;
10226
10227       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10228
10229       DisplayGameControlValues();
10230
10231       break;
10232     }
10233
10234     case CA_SET_LEVEL_WIND:
10235     {
10236       game.wind_direction = action_arg_direction;
10237
10238       break;
10239     }
10240
10241     case CA_SET_LEVEL_RANDOM_SEED:
10242     {
10243       // ensure that setting a new random seed while playing is predictable
10244       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10245
10246       break;
10247     }
10248
10249     // ---------- player actions  ---------------------------------------------
10250
10251     case CA_MOVE_PLAYER:
10252     case CA_MOVE_PLAYER_NEW:
10253     {
10254       // automatically move to the next field in specified direction
10255       for (i = 0; i < MAX_PLAYERS; i++)
10256         if (trigger_player_bits & (1 << i))
10257           if (action_type == CA_MOVE_PLAYER ||
10258               stored_player[i].MovPos == 0)
10259             stored_player[i].programmed_action = action_arg_direction;
10260
10261       break;
10262     }
10263
10264     case CA_EXIT_PLAYER:
10265     {
10266       for (i = 0; i < MAX_PLAYERS; i++)
10267         if (action_arg_player_bits & (1 << i))
10268           ExitPlayer(&stored_player[i]);
10269
10270       if (game.players_still_needed == 0)
10271         LevelSolved();
10272
10273       break;
10274     }
10275
10276     case CA_KILL_PLAYER:
10277     {
10278       for (i = 0; i < MAX_PLAYERS; i++)
10279         if (action_arg_player_bits & (1 << i))
10280           KillPlayer(&stored_player[i]);
10281
10282       break;
10283     }
10284
10285     case CA_SET_PLAYER_KEYS:
10286     {
10287       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10288       int element = getSpecialActionElement(action_arg_element,
10289                                             action_arg_number, EL_KEY_1);
10290
10291       if (IS_KEY(element))
10292       {
10293         for (i = 0; i < MAX_PLAYERS; i++)
10294         {
10295           if (trigger_player_bits & (1 << i))
10296           {
10297             stored_player[i].key[KEY_NR(element)] = key_state;
10298
10299             DrawGameDoorValues();
10300           }
10301         }
10302       }
10303
10304       break;
10305     }
10306
10307     case CA_SET_PLAYER_SPEED:
10308     {
10309       for (i = 0; i < MAX_PLAYERS; i++)
10310       {
10311         if (trigger_player_bits & (1 << i))
10312         {
10313           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10314
10315           if (action_arg == CA_ARG_SPEED_FASTER &&
10316               stored_player[i].cannot_move)
10317           {
10318             action_arg_number = STEPSIZE_VERY_SLOW;
10319           }
10320           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10321                    action_arg == CA_ARG_SPEED_FASTER)
10322           {
10323             action_arg_number = 2;
10324             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10325                            CA_MODE_MULTIPLY);
10326           }
10327           else if (action_arg == CA_ARG_NUMBER_RESET)
10328           {
10329             action_arg_number = level.initial_player_stepsize[i];
10330           }
10331
10332           move_stepsize =
10333             getModifiedActionNumber(move_stepsize,
10334                                     action_mode,
10335                                     action_arg_number,
10336                                     action_arg_number_min,
10337                                     action_arg_number_max);
10338
10339           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10340         }
10341       }
10342
10343       break;
10344     }
10345
10346     case CA_SET_PLAYER_SHIELD:
10347     {
10348       for (i = 0; i < MAX_PLAYERS; i++)
10349       {
10350         if (trigger_player_bits & (1 << i))
10351         {
10352           if (action_arg == CA_ARG_SHIELD_OFF)
10353           {
10354             stored_player[i].shield_normal_time_left = 0;
10355             stored_player[i].shield_deadly_time_left = 0;
10356           }
10357           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10358           {
10359             stored_player[i].shield_normal_time_left = 999999;
10360           }
10361           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10362           {
10363             stored_player[i].shield_normal_time_left = 999999;
10364             stored_player[i].shield_deadly_time_left = 999999;
10365           }
10366         }
10367       }
10368
10369       break;
10370     }
10371
10372     case CA_SET_PLAYER_GRAVITY:
10373     {
10374       for (i = 0; i < MAX_PLAYERS; i++)
10375       {
10376         if (trigger_player_bits & (1 << i))
10377         {
10378           stored_player[i].gravity =
10379             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10380              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10381              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10382              stored_player[i].gravity);
10383         }
10384       }
10385
10386       break;
10387     }
10388
10389     case CA_SET_PLAYER_ARTWORK:
10390     {
10391       for (i = 0; i < MAX_PLAYERS; i++)
10392       {
10393         if (trigger_player_bits & (1 << i))
10394         {
10395           int artwork_element = action_arg_element;
10396
10397           if (action_arg == CA_ARG_ELEMENT_RESET)
10398             artwork_element =
10399               (level.use_artwork_element[i] ? level.artwork_element[i] :
10400                stored_player[i].element_nr);
10401
10402           if (stored_player[i].artwork_element != artwork_element)
10403             stored_player[i].Frame = 0;
10404
10405           stored_player[i].artwork_element = artwork_element;
10406
10407           SetPlayerWaiting(&stored_player[i], FALSE);
10408
10409           // set number of special actions for bored and sleeping animation
10410           stored_player[i].num_special_action_bored =
10411             get_num_special_action(artwork_element,
10412                                    ACTION_BORING_1, ACTION_BORING_LAST);
10413           stored_player[i].num_special_action_sleeping =
10414             get_num_special_action(artwork_element,
10415                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10416         }
10417       }
10418
10419       break;
10420     }
10421
10422     case CA_SET_PLAYER_INVENTORY:
10423     {
10424       for (i = 0; i < MAX_PLAYERS; i++)
10425       {
10426         struct PlayerInfo *player = &stored_player[i];
10427         int j, k;
10428
10429         if (trigger_player_bits & (1 << i))
10430         {
10431           int inventory_element = action_arg_element;
10432
10433           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10434               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10435               action_arg == CA_ARG_ELEMENT_ACTION)
10436           {
10437             int element = inventory_element;
10438             int collect_count = element_info[element].collect_count_initial;
10439
10440             if (!IS_CUSTOM_ELEMENT(element))
10441               collect_count = 1;
10442
10443             if (collect_count == 0)
10444               player->inventory_infinite_element = element;
10445             else
10446               for (k = 0; k < collect_count; k++)
10447                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10448                   player->inventory_element[player->inventory_size++] =
10449                     element;
10450           }
10451           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10452                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10453                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10454           {
10455             if (player->inventory_infinite_element != EL_UNDEFINED &&
10456                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10457                                      action_arg_element_raw))
10458               player->inventory_infinite_element = EL_UNDEFINED;
10459
10460             for (k = 0, j = 0; j < player->inventory_size; j++)
10461             {
10462               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10463                                         action_arg_element_raw))
10464                 player->inventory_element[k++] = player->inventory_element[j];
10465             }
10466
10467             player->inventory_size = k;
10468           }
10469           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10470           {
10471             if (player->inventory_size > 0)
10472             {
10473               for (j = 0; j < player->inventory_size - 1; j++)
10474                 player->inventory_element[j] = player->inventory_element[j + 1];
10475
10476               player->inventory_size--;
10477             }
10478           }
10479           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10480           {
10481             if (player->inventory_size > 0)
10482               player->inventory_size--;
10483           }
10484           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10485           {
10486             player->inventory_infinite_element = EL_UNDEFINED;
10487             player->inventory_size = 0;
10488           }
10489           else if (action_arg == CA_ARG_INVENTORY_RESET)
10490           {
10491             player->inventory_infinite_element = EL_UNDEFINED;
10492             player->inventory_size = 0;
10493
10494             if (level.use_initial_inventory[i])
10495             {
10496               for (j = 0; j < level.initial_inventory_size[i]; j++)
10497               {
10498                 int element = level.initial_inventory_content[i][j];
10499                 int collect_count = element_info[element].collect_count_initial;
10500
10501                 if (!IS_CUSTOM_ELEMENT(element))
10502                   collect_count = 1;
10503
10504                 if (collect_count == 0)
10505                   player->inventory_infinite_element = element;
10506                 else
10507                   for (k = 0; k < collect_count; k++)
10508                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10509                       player->inventory_element[player->inventory_size++] =
10510                         element;
10511               }
10512             }
10513           }
10514         }
10515       }
10516
10517       break;
10518     }
10519
10520     // ---------- CE actions  -------------------------------------------------
10521
10522     case CA_SET_CE_VALUE:
10523     {
10524       int last_ce_value = CustomValue[x][y];
10525
10526       CustomValue[x][y] = action_arg_number_new;
10527
10528       if (CustomValue[x][y] != last_ce_value)
10529       {
10530         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10531         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10532
10533         if (CustomValue[x][y] == 0)
10534         {
10535           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10536           ChangeCount[x][y] = 0;        // allow at least one more change
10537
10538           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10539           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10540         }
10541       }
10542
10543       break;
10544     }
10545
10546     case CA_SET_CE_SCORE:
10547     {
10548       int last_ce_score = ei->collect_score;
10549
10550       ei->collect_score = action_arg_number_new;
10551
10552       if (ei->collect_score != last_ce_score)
10553       {
10554         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10555         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10556
10557         if (ei->collect_score == 0)
10558         {
10559           int xx, yy;
10560
10561           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10562           ChangeCount[x][y] = 0;        // allow at least one more change
10563
10564           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10565           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10566
10567           /*
10568             This is a very special case that seems to be a mixture between
10569             CheckElementChange() and CheckTriggeredElementChange(): while
10570             the first one only affects single elements that are triggered
10571             directly, the second one affects multiple elements in the playfield
10572             that are triggered indirectly by another element. This is a third
10573             case: Changing the CE score always affects multiple identical CEs,
10574             so every affected CE must be checked, not only the single CE for
10575             which the CE score was changed in the first place (as every instance
10576             of that CE shares the same CE score, and therefore also can change)!
10577           */
10578           SCAN_PLAYFIELD(xx, yy)
10579           {
10580             if (Tile[xx][yy] == element)
10581               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10582                                  CE_SCORE_GETS_ZERO);
10583           }
10584         }
10585       }
10586
10587       break;
10588     }
10589
10590     case CA_SET_CE_ARTWORK:
10591     {
10592       int artwork_element = action_arg_element;
10593       boolean reset_frame = FALSE;
10594       int xx, yy;
10595
10596       if (action_arg == CA_ARG_ELEMENT_RESET)
10597         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10598                            element);
10599
10600       if (ei->gfx_element != artwork_element)
10601         reset_frame = TRUE;
10602
10603       ei->gfx_element = artwork_element;
10604
10605       SCAN_PLAYFIELD(xx, yy)
10606       {
10607         if (Tile[xx][yy] == element)
10608         {
10609           if (reset_frame)
10610           {
10611             ResetGfxAnimation(xx, yy);
10612             ResetRandomAnimationValue(xx, yy);
10613           }
10614
10615           TEST_DrawLevelField(xx, yy);
10616         }
10617       }
10618
10619       break;
10620     }
10621
10622     // ---------- engine actions  ---------------------------------------------
10623
10624     case CA_SET_ENGINE_SCAN_MODE:
10625     {
10626       InitPlayfieldScanMode(action_arg);
10627
10628       break;
10629     }
10630
10631     default:
10632       break;
10633   }
10634 }
10635
10636 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10637 {
10638   int old_element = Tile[x][y];
10639   int new_element = GetElementFromGroupElement(element);
10640   int previous_move_direction = MovDir[x][y];
10641   int last_ce_value = CustomValue[x][y];
10642   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10643   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10644   boolean add_player_onto_element = (new_element_is_player &&
10645                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10646                                      IS_WALKABLE(old_element));
10647
10648   if (!add_player_onto_element)
10649   {
10650     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10651       RemoveMovingField(x, y);
10652     else
10653       RemoveField(x, y);
10654
10655     Tile[x][y] = new_element;
10656
10657     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10658       MovDir[x][y] = previous_move_direction;
10659
10660     if (element_info[new_element].use_last_ce_value)
10661       CustomValue[x][y] = last_ce_value;
10662
10663     InitField_WithBug1(x, y, FALSE);
10664
10665     new_element = Tile[x][y];   // element may have changed
10666
10667     ResetGfxAnimation(x, y);
10668     ResetRandomAnimationValue(x, y);
10669
10670     TEST_DrawLevelField(x, y);
10671
10672     if (GFX_CRUMBLED(new_element))
10673       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10674   }
10675
10676   // check if element under the player changes from accessible to unaccessible
10677   // (needed for special case of dropping element which then changes)
10678   // (must be checked after creating new element for walkable group elements)
10679   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10680       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10681   {
10682     Bang(x, y);
10683
10684     return;
10685   }
10686
10687   // "ChangeCount" not set yet to allow "entered by player" change one time
10688   if (new_element_is_player)
10689     RelocatePlayer(x, y, new_element);
10690
10691   if (is_change)
10692     ChangeCount[x][y]++;        // count number of changes in the same frame
10693
10694   TestIfBadThingTouchesPlayer(x, y);
10695   TestIfPlayerTouchesCustomElement(x, y);
10696   TestIfElementTouchesCustomElement(x, y);
10697 }
10698
10699 static void CreateField(int x, int y, int element)
10700 {
10701   CreateFieldExt(x, y, element, FALSE);
10702 }
10703
10704 static void CreateElementFromChange(int x, int y, int element)
10705 {
10706   element = GET_VALID_RUNTIME_ELEMENT(element);
10707
10708   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10709   {
10710     int old_element = Tile[x][y];
10711
10712     // prevent changed element from moving in same engine frame
10713     // unless both old and new element can either fall or move
10714     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10715         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10716       Stop[x][y] = TRUE;
10717   }
10718
10719   CreateFieldExt(x, y, element, TRUE);
10720 }
10721
10722 static boolean ChangeElement(int x, int y, int element, int page)
10723 {
10724   struct ElementInfo *ei = &element_info[element];
10725   struct ElementChangeInfo *change = &ei->change_page[page];
10726   int ce_value = CustomValue[x][y];
10727   int ce_score = ei->collect_score;
10728   int target_element;
10729   int old_element = Tile[x][y];
10730
10731   // always use default change event to prevent running into a loop
10732   if (ChangeEvent[x][y] == -1)
10733     ChangeEvent[x][y] = CE_DELAY;
10734
10735   if (ChangeEvent[x][y] == CE_DELAY)
10736   {
10737     // reset actual trigger element, trigger player and action element
10738     change->actual_trigger_element = EL_EMPTY;
10739     change->actual_trigger_player = EL_EMPTY;
10740     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10741     change->actual_trigger_side = CH_SIDE_NONE;
10742     change->actual_trigger_ce_value = 0;
10743     change->actual_trigger_ce_score = 0;
10744   }
10745
10746   // do not change elements more than a specified maximum number of changes
10747   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10748     return FALSE;
10749
10750   ChangeCount[x][y]++;          // count number of changes in the same frame
10751
10752   if (change->explode)
10753   {
10754     Bang(x, y);
10755
10756     return TRUE;
10757   }
10758
10759   if (change->use_target_content)
10760   {
10761     boolean complete_replace = TRUE;
10762     boolean can_replace[3][3];
10763     int xx, yy;
10764
10765     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10766     {
10767       boolean is_empty;
10768       boolean is_walkable;
10769       boolean is_diggable;
10770       boolean is_collectible;
10771       boolean is_removable;
10772       boolean is_destructible;
10773       int ex = x + xx - 1;
10774       int ey = y + yy - 1;
10775       int content_element = change->target_content.e[xx][yy];
10776       int e;
10777
10778       can_replace[xx][yy] = TRUE;
10779
10780       if (ex == x && ey == y)   // do not check changing element itself
10781         continue;
10782
10783       if (content_element == EL_EMPTY_SPACE)
10784       {
10785         can_replace[xx][yy] = FALSE;    // do not replace border with space
10786
10787         continue;
10788       }
10789
10790       if (!IN_LEV_FIELD(ex, ey))
10791       {
10792         can_replace[xx][yy] = FALSE;
10793         complete_replace = FALSE;
10794
10795         continue;
10796       }
10797
10798       e = Tile[ex][ey];
10799
10800       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10801         e = MovingOrBlocked2Element(ex, ey);
10802
10803       is_empty = (IS_FREE(ex, ey) ||
10804                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10805
10806       is_walkable     = (is_empty || IS_WALKABLE(e));
10807       is_diggable     = (is_empty || IS_DIGGABLE(e));
10808       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10809       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10810       is_removable    = (is_diggable || is_collectible);
10811
10812       can_replace[xx][yy] =
10813         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10814           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10815           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10816           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10817           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10818           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10819          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10820
10821       if (!can_replace[xx][yy])
10822         complete_replace = FALSE;
10823     }
10824
10825     if (!change->only_if_complete || complete_replace)
10826     {
10827       boolean something_has_changed = FALSE;
10828
10829       if (change->only_if_complete && change->use_random_replace &&
10830           RND(100) < change->random_percentage)
10831         return FALSE;
10832
10833       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10834       {
10835         int ex = x + xx - 1;
10836         int ey = y + yy - 1;
10837         int content_element;
10838
10839         if (can_replace[xx][yy] && (!change->use_random_replace ||
10840                                     RND(100) < change->random_percentage))
10841         {
10842           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10843             RemoveMovingField(ex, ey);
10844
10845           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10846
10847           content_element = change->target_content.e[xx][yy];
10848           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10849                                               ce_value, ce_score);
10850
10851           CreateElementFromChange(ex, ey, target_element);
10852
10853           something_has_changed = TRUE;
10854
10855           // for symmetry reasons, freeze newly created border elements
10856           if (ex != x || ey != y)
10857             Stop[ex][ey] = TRUE;        // no more moving in this frame
10858         }
10859       }
10860
10861       if (something_has_changed)
10862       {
10863         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10864         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10865       }
10866     }
10867   }
10868   else
10869   {
10870     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10871                                         ce_value, ce_score);
10872
10873     if (element == EL_DIAGONAL_GROWING ||
10874         element == EL_DIAGONAL_SHRINKING)
10875     {
10876       target_element = Store[x][y];
10877
10878       Store[x][y] = EL_EMPTY;
10879     }
10880
10881     // special case: element changes to player (and may be kept if walkable)
10882     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10883       CreateElementFromChange(x, y, EL_EMPTY);
10884
10885     CreateElementFromChange(x, y, target_element);
10886
10887     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10888     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10889   }
10890
10891   // this uses direct change before indirect change
10892   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10893
10894   return TRUE;
10895 }
10896
10897 static void HandleElementChange(int x, int y, int page)
10898 {
10899   int element = MovingOrBlocked2Element(x, y);
10900   struct ElementInfo *ei = &element_info[element];
10901   struct ElementChangeInfo *change = &ei->change_page[page];
10902   boolean handle_action_before_change = FALSE;
10903
10904 #ifdef DEBUG
10905   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10906       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10907   {
10908     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10909           x, y, element, element_info[element].token_name);
10910     Debug("game:playing:HandleElementChange", "This should never happen!");
10911   }
10912 #endif
10913
10914   // this can happen with classic bombs on walkable, changing elements
10915   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10916   {
10917     return;
10918   }
10919
10920   if (ChangeDelay[x][y] == 0)           // initialize element change
10921   {
10922     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10923
10924     if (change->can_change)
10925     {
10926       // !!! not clear why graphic animation should be reset at all here !!!
10927       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10928       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10929
10930       /*
10931         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10932
10933         When using an animation frame delay of 1 (this only happens with
10934         "sp_zonk.moving.left/right" in the classic graphics), the default
10935         (non-moving) animation shows wrong animation frames (while the
10936         moving animation, like "sp_zonk.moving.left/right", is correct,
10937         so this graphical bug never shows up with the classic graphics).
10938         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10939         be drawn instead of the correct frames 0,1,2,3. This is caused by
10940         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10941         an element change: First when the change delay ("ChangeDelay[][]")
10942         counter has reached zero after decrementing, then a second time in
10943         the next frame (after "GfxFrame[][]" was already incremented) when
10944         "ChangeDelay[][]" is reset to the initial delay value again.
10945
10946         This causes frame 0 to be drawn twice, while the last frame won't
10947         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10948
10949         As some animations may already be cleverly designed around this bug
10950         (at least the "Snake Bite" snake tail animation does this), it cannot
10951         simply be fixed here without breaking such existing animations.
10952         Unfortunately, it cannot easily be detected if a graphics set was
10953         designed "before" or "after" the bug was fixed. As a workaround,
10954         a new graphics set option "game.graphics_engine_version" was added
10955         to be able to specify the game's major release version for which the
10956         graphics set was designed, which can then be used to decide if the
10957         bugfix should be used (version 4 and above) or not (version 3 or
10958         below, or if no version was specified at all, as with old sets).
10959
10960         (The wrong/fixed animation frames can be tested with the test level set
10961         "test_gfxframe" and level "000", which contains a specially prepared
10962         custom element at level position (x/y) == (11/9) which uses the zonk
10963         animation mentioned above. Using "game.graphics_engine_version: 4"
10964         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10965         This can also be seen from the debug output for this test element.)
10966       */
10967
10968       // when a custom element is about to change (for example by change delay),
10969       // do not reset graphic animation when the custom element is moving
10970       if (game.graphics_engine_version < 4 &&
10971           !IS_MOVING(x, y))
10972       {
10973         ResetGfxAnimation(x, y);
10974         ResetRandomAnimationValue(x, y);
10975       }
10976
10977       if (change->pre_change_function)
10978         change->pre_change_function(x, y);
10979     }
10980   }
10981
10982   ChangeDelay[x][y]--;
10983
10984   if (ChangeDelay[x][y] != 0)           // continue element change
10985   {
10986     if (change->can_change)
10987     {
10988       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10989
10990       if (IS_ANIMATED(graphic))
10991         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10992
10993       if (change->change_function)
10994         change->change_function(x, y);
10995     }
10996   }
10997   else                                  // finish element change
10998   {
10999     if (ChangePage[x][y] != -1)         // remember page from delayed change
11000     {
11001       page = ChangePage[x][y];
11002       ChangePage[x][y] = -1;
11003
11004       change = &ei->change_page[page];
11005     }
11006
11007     if (IS_MOVING(x, y))                // never change a running system ;-)
11008     {
11009       ChangeDelay[x][y] = 1;            // try change after next move step
11010       ChangePage[x][y] = page;          // remember page to use for change
11011
11012       return;
11013     }
11014
11015     // special case: set new level random seed before changing element
11016     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11017       handle_action_before_change = TRUE;
11018
11019     if (change->has_action && handle_action_before_change)
11020       ExecuteCustomElementAction(x, y, element, page);
11021
11022     if (change->can_change)
11023     {
11024       if (ChangeElement(x, y, element, page))
11025       {
11026         if (change->post_change_function)
11027           change->post_change_function(x, y);
11028       }
11029     }
11030
11031     if (change->has_action && !handle_action_before_change)
11032       ExecuteCustomElementAction(x, y, element, page);
11033   }
11034 }
11035
11036 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11037                                               int trigger_element,
11038                                               int trigger_event,
11039                                               int trigger_player,
11040                                               int trigger_side,
11041                                               int trigger_page)
11042 {
11043   boolean change_done_any = FALSE;
11044   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11045   int i;
11046
11047   if (!(trigger_events[trigger_element][trigger_event]))
11048     return FALSE;
11049
11050   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11051
11052   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11053   {
11054     int element = EL_CUSTOM_START + i;
11055     boolean change_done = FALSE;
11056     int p;
11057
11058     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11059         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11060       continue;
11061
11062     for (p = 0; p < element_info[element].num_change_pages; p++)
11063     {
11064       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11065
11066       if (change->can_change_or_has_action &&
11067           change->has_event[trigger_event] &&
11068           change->trigger_side & trigger_side &&
11069           change->trigger_player & trigger_player &&
11070           change->trigger_page & trigger_page_bits &&
11071           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11072       {
11073         change->actual_trigger_element = trigger_element;
11074         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11075         change->actual_trigger_player_bits = trigger_player;
11076         change->actual_trigger_side = trigger_side;
11077         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11078         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11079
11080         if ((change->can_change && !change_done) || change->has_action)
11081         {
11082           int x, y;
11083
11084           SCAN_PLAYFIELD(x, y)
11085           {
11086             if (Tile[x][y] == element)
11087             {
11088               if (change->can_change && !change_done)
11089               {
11090                 // if element already changed in this frame, not only prevent
11091                 // another element change (checked in ChangeElement()), but
11092                 // also prevent additional element actions for this element
11093
11094                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11095                     !level.use_action_after_change_bug)
11096                   continue;
11097
11098                 ChangeDelay[x][y] = 1;
11099                 ChangeEvent[x][y] = trigger_event;
11100
11101                 HandleElementChange(x, y, p);
11102               }
11103               else if (change->has_action)
11104               {
11105                 // if element already changed in this frame, not only prevent
11106                 // another element change (checked in ChangeElement()), but
11107                 // also prevent additional element actions for this element
11108
11109                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11110                     !level.use_action_after_change_bug)
11111                   continue;
11112
11113                 ExecuteCustomElementAction(x, y, element, p);
11114                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11115               }
11116             }
11117           }
11118
11119           if (change->can_change)
11120           {
11121             change_done = TRUE;
11122             change_done_any = TRUE;
11123           }
11124         }
11125       }
11126     }
11127   }
11128
11129   RECURSION_LOOP_DETECTION_END();
11130
11131   return change_done_any;
11132 }
11133
11134 static boolean CheckElementChangeExt(int x, int y,
11135                                      int element,
11136                                      int trigger_element,
11137                                      int trigger_event,
11138                                      int trigger_player,
11139                                      int trigger_side)
11140 {
11141   boolean change_done = FALSE;
11142   int p;
11143
11144   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11145       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11146     return FALSE;
11147
11148   if (Tile[x][y] == EL_BLOCKED)
11149   {
11150     Blocked2Moving(x, y, &x, &y);
11151     element = Tile[x][y];
11152   }
11153
11154   // check if element has already changed or is about to change after moving
11155   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11156        Tile[x][y] != element) ||
11157
11158       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11159        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11160         ChangePage[x][y] != -1)))
11161     return FALSE;
11162
11163   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11164
11165   for (p = 0; p < element_info[element].num_change_pages; p++)
11166   {
11167     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11168
11169     /* check trigger element for all events where the element that is checked
11170        for changing interacts with a directly adjacent element -- this is
11171        different to element changes that affect other elements to change on the
11172        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11173     boolean check_trigger_element =
11174       (trigger_event == CE_NEXT_TO_X ||
11175        trigger_event == CE_TOUCHING_X ||
11176        trigger_event == CE_HITTING_X ||
11177        trigger_event == CE_HIT_BY_X ||
11178        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11179
11180     if (change->can_change_or_has_action &&
11181         change->has_event[trigger_event] &&
11182         change->trigger_side & trigger_side &&
11183         change->trigger_player & trigger_player &&
11184         (!check_trigger_element ||
11185          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11186     {
11187       change->actual_trigger_element = trigger_element;
11188       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11189       change->actual_trigger_player_bits = trigger_player;
11190       change->actual_trigger_side = trigger_side;
11191       change->actual_trigger_ce_value = CustomValue[x][y];
11192       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11193
11194       // special case: trigger element not at (x,y) position for some events
11195       if (check_trigger_element)
11196       {
11197         static struct
11198         {
11199           int dx, dy;
11200         } move_xy[] =
11201           {
11202             {  0,  0 },
11203             { -1,  0 },
11204             { +1,  0 },
11205             {  0,  0 },
11206             {  0, -1 },
11207             {  0,  0 }, { 0, 0 }, { 0, 0 },
11208             {  0, +1 }
11209           };
11210
11211         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11212         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11213
11214         change->actual_trigger_ce_value = CustomValue[xx][yy];
11215         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11216       }
11217
11218       if (change->can_change && !change_done)
11219       {
11220         ChangeDelay[x][y] = 1;
11221         ChangeEvent[x][y] = trigger_event;
11222
11223         HandleElementChange(x, y, p);
11224
11225         change_done = TRUE;
11226       }
11227       else if (change->has_action)
11228       {
11229         ExecuteCustomElementAction(x, y, element, p);
11230         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11231       }
11232     }
11233   }
11234
11235   RECURSION_LOOP_DETECTION_END();
11236
11237   return change_done;
11238 }
11239
11240 static void PlayPlayerSound(struct PlayerInfo *player)
11241 {
11242   int jx = player->jx, jy = player->jy;
11243   int sound_element = player->artwork_element;
11244   int last_action = player->last_action_waiting;
11245   int action = player->action_waiting;
11246
11247   if (player->is_waiting)
11248   {
11249     if (action != last_action)
11250       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11251     else
11252       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11253   }
11254   else
11255   {
11256     if (action != last_action)
11257       StopSound(element_info[sound_element].sound[last_action]);
11258
11259     if (last_action == ACTION_SLEEPING)
11260       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11261   }
11262 }
11263
11264 static void PlayAllPlayersSound(void)
11265 {
11266   int i;
11267
11268   for (i = 0; i < MAX_PLAYERS; i++)
11269     if (stored_player[i].active)
11270       PlayPlayerSound(&stored_player[i]);
11271 }
11272
11273 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11274 {
11275   boolean last_waiting = player->is_waiting;
11276   int move_dir = player->MovDir;
11277
11278   player->dir_waiting = move_dir;
11279   player->last_action_waiting = player->action_waiting;
11280
11281   if (is_waiting)
11282   {
11283     if (!last_waiting)          // not waiting -> waiting
11284     {
11285       player->is_waiting = TRUE;
11286
11287       player->frame_counter_bored =
11288         FrameCounter +
11289         game.player_boring_delay_fixed +
11290         GetSimpleRandom(game.player_boring_delay_random);
11291       player->frame_counter_sleeping =
11292         FrameCounter +
11293         game.player_sleeping_delay_fixed +
11294         GetSimpleRandom(game.player_sleeping_delay_random);
11295
11296       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11297     }
11298
11299     if (game.player_sleeping_delay_fixed +
11300         game.player_sleeping_delay_random > 0 &&
11301         player->anim_delay_counter == 0 &&
11302         player->post_delay_counter == 0 &&
11303         FrameCounter >= player->frame_counter_sleeping)
11304       player->is_sleeping = TRUE;
11305     else if (game.player_boring_delay_fixed +
11306              game.player_boring_delay_random > 0 &&
11307              FrameCounter >= player->frame_counter_bored)
11308       player->is_bored = TRUE;
11309
11310     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11311                               player->is_bored ? ACTION_BORING :
11312                               ACTION_WAITING);
11313
11314     if (player->is_sleeping && player->use_murphy)
11315     {
11316       // special case for sleeping Murphy when leaning against non-free tile
11317
11318       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11319           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11320            !IS_MOVING(player->jx - 1, player->jy)))
11321         move_dir = MV_LEFT;
11322       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11323                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11324                 !IS_MOVING(player->jx + 1, player->jy)))
11325         move_dir = MV_RIGHT;
11326       else
11327         player->is_sleeping = FALSE;
11328
11329       player->dir_waiting = move_dir;
11330     }
11331
11332     if (player->is_sleeping)
11333     {
11334       if (player->num_special_action_sleeping > 0)
11335       {
11336         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11337         {
11338           int last_special_action = player->special_action_sleeping;
11339           int num_special_action = player->num_special_action_sleeping;
11340           int special_action =
11341             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11342              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11343              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11344              last_special_action + 1 : ACTION_SLEEPING);
11345           int special_graphic =
11346             el_act_dir2img(player->artwork_element, special_action, move_dir);
11347
11348           player->anim_delay_counter =
11349             graphic_info[special_graphic].anim_delay_fixed +
11350             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11351           player->post_delay_counter =
11352             graphic_info[special_graphic].post_delay_fixed +
11353             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11354
11355           player->special_action_sleeping = special_action;
11356         }
11357
11358         if (player->anim_delay_counter > 0)
11359         {
11360           player->action_waiting = player->special_action_sleeping;
11361           player->anim_delay_counter--;
11362         }
11363         else if (player->post_delay_counter > 0)
11364         {
11365           player->post_delay_counter--;
11366         }
11367       }
11368     }
11369     else if (player->is_bored)
11370     {
11371       if (player->num_special_action_bored > 0)
11372       {
11373         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11374         {
11375           int special_action =
11376             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11377           int special_graphic =
11378             el_act_dir2img(player->artwork_element, special_action, move_dir);
11379
11380           player->anim_delay_counter =
11381             graphic_info[special_graphic].anim_delay_fixed +
11382             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11383           player->post_delay_counter =
11384             graphic_info[special_graphic].post_delay_fixed +
11385             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11386
11387           player->special_action_bored = special_action;
11388         }
11389
11390         if (player->anim_delay_counter > 0)
11391         {
11392           player->action_waiting = player->special_action_bored;
11393           player->anim_delay_counter--;
11394         }
11395         else if (player->post_delay_counter > 0)
11396         {
11397           player->post_delay_counter--;
11398         }
11399       }
11400     }
11401   }
11402   else if (last_waiting)        // waiting -> not waiting
11403   {
11404     player->is_waiting = FALSE;
11405     player->is_bored = FALSE;
11406     player->is_sleeping = FALSE;
11407
11408     player->frame_counter_bored = -1;
11409     player->frame_counter_sleeping = -1;
11410
11411     player->anim_delay_counter = 0;
11412     player->post_delay_counter = 0;
11413
11414     player->dir_waiting = player->MovDir;
11415     player->action_waiting = ACTION_DEFAULT;
11416
11417     player->special_action_bored = ACTION_DEFAULT;
11418     player->special_action_sleeping = ACTION_DEFAULT;
11419   }
11420 }
11421
11422 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11423 {
11424   if ((!player->is_moving  && player->was_moving) ||
11425       (player->MovPos == 0 && player->was_moving) ||
11426       (player->is_snapping && !player->was_snapping) ||
11427       (player->is_dropping && !player->was_dropping))
11428   {
11429     if (!CheckSaveEngineSnapshotToList())
11430       return;
11431
11432     player->was_moving = FALSE;
11433     player->was_snapping = TRUE;
11434     player->was_dropping = TRUE;
11435   }
11436   else
11437   {
11438     if (player->is_moving)
11439       player->was_moving = TRUE;
11440
11441     if (!player->is_snapping)
11442       player->was_snapping = FALSE;
11443
11444     if (!player->is_dropping)
11445       player->was_dropping = FALSE;
11446   }
11447
11448   static struct MouseActionInfo mouse_action_last = { 0 };
11449   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11450   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11451
11452   if (new_released)
11453     CheckSaveEngineSnapshotToList();
11454
11455   mouse_action_last = mouse_action;
11456 }
11457
11458 static void CheckSingleStepMode(struct PlayerInfo *player)
11459 {
11460   if (tape.single_step && tape.recording && !tape.pausing)
11461   {
11462     // as it is called "single step mode", just return to pause mode when the
11463     // player stopped moving after one tile (or never starts moving at all)
11464     // (reverse logic needed here in case single step mode used in team mode)
11465     if (player->is_moving ||
11466         player->is_pushing ||
11467         player->is_dropping_pressed ||
11468         player->effective_mouse_action.button)
11469       game.enter_single_step_mode = FALSE;
11470   }
11471
11472   CheckSaveEngineSnapshot(player);
11473 }
11474
11475 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11476 {
11477   int left      = player_action & JOY_LEFT;
11478   int right     = player_action & JOY_RIGHT;
11479   int up        = player_action & JOY_UP;
11480   int down      = player_action & JOY_DOWN;
11481   int button1   = player_action & JOY_BUTTON_1;
11482   int button2   = player_action & JOY_BUTTON_2;
11483   int dx        = (left ? -1 : right ? 1 : 0);
11484   int dy        = (up   ? -1 : down  ? 1 : 0);
11485
11486   if (!player->active || tape.pausing)
11487     return 0;
11488
11489   if (player_action)
11490   {
11491     if (button1)
11492       SnapField(player, dx, dy);
11493     else
11494     {
11495       if (button2)
11496         DropElement(player);
11497
11498       MovePlayer(player, dx, dy);
11499     }
11500
11501     CheckSingleStepMode(player);
11502
11503     SetPlayerWaiting(player, FALSE);
11504
11505     return player_action;
11506   }
11507   else
11508   {
11509     // no actions for this player (no input at player's configured device)
11510
11511     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11512     SnapField(player, 0, 0);
11513     CheckGravityMovementWhenNotMoving(player);
11514
11515     if (player->MovPos == 0)
11516       SetPlayerWaiting(player, TRUE);
11517
11518     if (player->MovPos == 0)    // needed for tape.playing
11519       player->is_moving = FALSE;
11520
11521     player->is_dropping = FALSE;
11522     player->is_dropping_pressed = FALSE;
11523     player->drop_pressed_delay = 0;
11524
11525     CheckSingleStepMode(player);
11526
11527     return 0;
11528   }
11529 }
11530
11531 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11532                                          byte *tape_action)
11533 {
11534   if (!tape.use_mouse_actions)
11535     return;
11536
11537   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11538   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11539   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11540 }
11541
11542 static void SetTapeActionFromMouseAction(byte *tape_action,
11543                                          struct MouseActionInfo *mouse_action)
11544 {
11545   if (!tape.use_mouse_actions)
11546     return;
11547
11548   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11549   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11550   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11551 }
11552
11553 static void CheckLevelSolved(void)
11554 {
11555   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11556   {
11557     if (game_em.level_solved &&
11558         !game_em.game_over)                             // game won
11559     {
11560       LevelSolved();
11561
11562       game_em.game_over = TRUE;
11563
11564       game.all_players_gone = TRUE;
11565     }
11566
11567     if (game_em.game_over)                              // game lost
11568       game.all_players_gone = TRUE;
11569   }
11570   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11571   {
11572     if (game_sp.level_solved &&
11573         !game_sp.game_over)                             // game won
11574     {
11575       LevelSolved();
11576
11577       game_sp.game_over = TRUE;
11578
11579       game.all_players_gone = TRUE;
11580     }
11581
11582     if (game_sp.game_over)                              // game lost
11583       game.all_players_gone = TRUE;
11584   }
11585   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11586   {
11587     if (game_mm.level_solved &&
11588         !game_mm.game_over)                             // game won
11589     {
11590       LevelSolved();
11591
11592       game_mm.game_over = TRUE;
11593
11594       game.all_players_gone = TRUE;
11595     }
11596
11597     if (game_mm.game_over)                              // game lost
11598       game.all_players_gone = TRUE;
11599   }
11600 }
11601
11602 static void CheckLevelTime_StepCounter(void)
11603 {
11604   int i;
11605
11606   TimePlayed++;
11607
11608   if (TimeLeft > 0)
11609   {
11610     TimeLeft--;
11611
11612     if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
11613       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11614
11615     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11616
11617     DisplayGameControlValues();
11618
11619     if (!TimeLeft && setup.time_limit && !game.LevelSolved)
11620       for (i = 0; i < MAX_PLAYERS; i++)
11621         KillPlayer(&stored_player[i]);
11622   }
11623   else if (game.no_time_limit && !game.all_players_gone)
11624   {
11625     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11626
11627     DisplayGameControlValues();
11628   }
11629 }
11630
11631 static void CheckLevelTime(void)
11632 {
11633   int i;
11634
11635   if (TimeFrames >= FRAMES_PER_SECOND)
11636   {
11637     TimeFrames = 0;
11638     TapeTime++;
11639
11640     for (i = 0; i < MAX_PLAYERS; i++)
11641     {
11642       struct PlayerInfo *player = &stored_player[i];
11643
11644       if (SHIELD_ON(player))
11645       {
11646         player->shield_normal_time_left--;
11647
11648         if (player->shield_deadly_time_left > 0)
11649           player->shield_deadly_time_left--;
11650       }
11651     }
11652
11653     if (!game.LevelSolved && !level.use_step_counter)
11654     {
11655       TimePlayed++;
11656
11657       if (TimeLeft > 0)
11658       {
11659         TimeLeft--;
11660
11661         if (TimeLeft <= 10 && setup.time_limit)
11662           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11663
11664         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11665            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11666
11667         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11668
11669         if (!TimeLeft && setup.time_limit)
11670         {
11671           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11672             game_em.lev->killed_out_of_time = TRUE;
11673           else
11674             for (i = 0; i < MAX_PLAYERS; i++)
11675               KillPlayer(&stored_player[i]);
11676         }
11677       }
11678       else if (game.no_time_limit && !game.all_players_gone)
11679       {
11680         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11681       }
11682
11683       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11684     }
11685
11686     if (tape.recording || tape.playing)
11687       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11688   }
11689
11690   if (tape.recording || tape.playing)
11691     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11692
11693   UpdateAndDisplayGameControlValues();
11694 }
11695
11696 void AdvanceFrameAndPlayerCounters(int player_nr)
11697 {
11698   int i;
11699
11700   // advance frame counters (global frame counter and time frame counter)
11701   FrameCounter++;
11702   TimeFrames++;
11703
11704   // advance player counters (counters for move delay, move animation etc.)
11705   for (i = 0; i < MAX_PLAYERS; i++)
11706   {
11707     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11708     int move_delay_value = stored_player[i].move_delay_value;
11709     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11710
11711     if (!advance_player_counters)       // not all players may be affected
11712       continue;
11713
11714     if (move_frames == 0)       // less than one move per game frame
11715     {
11716       int stepsize = TILEX / move_delay_value;
11717       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11718       int count = (stored_player[i].is_moving ?
11719                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11720
11721       if (count % delay == 0)
11722         move_frames = 1;
11723     }
11724
11725     stored_player[i].Frame += move_frames;
11726
11727     if (stored_player[i].MovPos != 0)
11728       stored_player[i].StepFrame += move_frames;
11729
11730     if (stored_player[i].move_delay > 0)
11731       stored_player[i].move_delay--;
11732
11733     // due to bugs in previous versions, counter must count up, not down
11734     if (stored_player[i].push_delay != -1)
11735       stored_player[i].push_delay++;
11736
11737     if (stored_player[i].drop_delay > 0)
11738       stored_player[i].drop_delay--;
11739
11740     if (stored_player[i].is_dropping_pressed)
11741       stored_player[i].drop_pressed_delay++;
11742   }
11743 }
11744
11745 void StartGameActions(boolean init_network_game, boolean record_tape,
11746                       int random_seed)
11747 {
11748   unsigned int new_random_seed = InitRND(random_seed);
11749
11750   if (record_tape)
11751     TapeStartRecording(new_random_seed);
11752
11753   if (init_network_game)
11754   {
11755     SendToServer_LevelFile();
11756     SendToServer_StartPlaying();
11757
11758     return;
11759   }
11760
11761   InitGame();
11762 }
11763
11764 static void GameActionsExt(void)
11765 {
11766 #if 0
11767   static unsigned int game_frame_delay = 0;
11768 #endif
11769   unsigned int game_frame_delay_value;
11770   byte *recorded_player_action;
11771   byte summarized_player_action = 0;
11772   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11773   int i;
11774
11775   // detect endless loops, caused by custom element programming
11776   if (recursion_loop_detected && recursion_loop_depth == 0)
11777   {
11778     char *message = getStringCat3("Internal Error! Element ",
11779                                   EL_NAME(recursion_loop_element),
11780                                   " caused endless loop! Quit the game?");
11781
11782     Warn("element '%s' caused endless loop in game engine",
11783          EL_NAME(recursion_loop_element));
11784
11785     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11786
11787     recursion_loop_detected = FALSE;    // if game should be continued
11788
11789     free(message);
11790
11791     return;
11792   }
11793
11794   if (game.restart_level)
11795     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11796
11797   CheckLevelSolved();
11798
11799   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11800     GameWon();
11801
11802   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11803     TapeStop();
11804
11805   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11806     return;
11807
11808   game_frame_delay_value =
11809     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11810
11811   if (tape.playing && tape.warp_forward && !tape.pausing)
11812     game_frame_delay_value = 0;
11813
11814   SetVideoFrameDelay(game_frame_delay_value);
11815
11816   // (de)activate virtual buttons depending on current game status
11817   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11818   {
11819     if (game.all_players_gone)  // if no players there to be controlled anymore
11820       SetOverlayActive(FALSE);
11821     else if (!tape.playing)     // if game continues after tape stopped playing
11822       SetOverlayActive(TRUE);
11823   }
11824
11825 #if 0
11826 #if 0
11827   // ---------- main game synchronization point ----------
11828
11829   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11830
11831   Debug("game:playing:skip", "skip == %d", skip);
11832
11833 #else
11834   // ---------- main game synchronization point ----------
11835
11836   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11837 #endif
11838 #endif
11839
11840   if (network_playing && !network_player_action_received)
11841   {
11842     // try to get network player actions in time
11843
11844     // last chance to get network player actions without main loop delay
11845     HandleNetworking();
11846
11847     // game was quit by network peer
11848     if (game_status != GAME_MODE_PLAYING)
11849       return;
11850
11851     // check if network player actions still missing and game still running
11852     if (!network_player_action_received && !checkGameEnded())
11853       return;           // failed to get network player actions in time
11854
11855     // do not yet reset "network_player_action_received" (for tape.pausing)
11856   }
11857
11858   if (tape.pausing)
11859     return;
11860
11861   // at this point we know that we really continue executing the game
11862
11863   network_player_action_received = FALSE;
11864
11865   // when playing tape, read previously recorded player input from tape data
11866   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11867
11868   local_player->effective_mouse_action = local_player->mouse_action;
11869
11870   if (recorded_player_action != NULL)
11871     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11872                                  recorded_player_action);
11873
11874   // TapePlayAction() may return NULL when toggling to "pause before death"
11875   if (tape.pausing)
11876     return;
11877
11878   if (tape.set_centered_player)
11879   {
11880     game.centered_player_nr_next = tape.centered_player_nr_next;
11881     game.set_centered_player = TRUE;
11882   }
11883
11884   for (i = 0; i < MAX_PLAYERS; i++)
11885   {
11886     summarized_player_action |= stored_player[i].action;
11887
11888     if (!network_playing && (game.team_mode || tape.playing))
11889       stored_player[i].effective_action = stored_player[i].action;
11890   }
11891
11892   if (network_playing && !checkGameEnded())
11893     SendToServer_MovePlayer(summarized_player_action);
11894
11895   // summarize all actions at local players mapped input device position
11896   // (this allows using different input devices in single player mode)
11897   if (!network.enabled && !game.team_mode)
11898     stored_player[map_player_action[local_player->index_nr]].effective_action =
11899       summarized_player_action;
11900
11901   // summarize all actions at centered player in local team mode
11902   if (tape.recording &&
11903       setup.team_mode && !network.enabled &&
11904       setup.input_on_focus &&
11905       game.centered_player_nr != -1)
11906   {
11907     for (i = 0; i < MAX_PLAYERS; i++)
11908       stored_player[map_player_action[i]].effective_action =
11909         (i == game.centered_player_nr ? summarized_player_action : 0);
11910   }
11911
11912   if (recorded_player_action != NULL)
11913     for (i = 0; i < MAX_PLAYERS; i++)
11914       stored_player[i].effective_action = recorded_player_action[i];
11915
11916   for (i = 0; i < MAX_PLAYERS; i++)
11917   {
11918     tape_action[i] = stored_player[i].effective_action;
11919
11920     /* (this may happen in the RND game engine if a player was not present on
11921        the playfield on level start, but appeared later from a custom element */
11922     if (setup.team_mode &&
11923         tape.recording &&
11924         tape_action[i] &&
11925         !tape.player_participates[i])
11926       tape.player_participates[i] = TRUE;
11927   }
11928
11929   SetTapeActionFromMouseAction(tape_action,
11930                                &local_player->effective_mouse_action);
11931
11932   // only record actions from input devices, but not programmed actions
11933   if (tape.recording)
11934     TapeRecordAction(tape_action);
11935
11936   // remember if game was played (especially after tape stopped playing)
11937   if (!tape.playing && summarized_player_action)
11938     game.GamePlayed = TRUE;
11939
11940 #if USE_NEW_PLAYER_ASSIGNMENTS
11941   // !!! also map player actions in single player mode !!!
11942   // if (game.team_mode)
11943   if (1)
11944   {
11945     byte mapped_action[MAX_PLAYERS];
11946
11947 #if DEBUG_PLAYER_ACTIONS
11948     for (i = 0; i < MAX_PLAYERS; i++)
11949       DebugContinued("", "%d, ", stored_player[i].effective_action);
11950 #endif
11951
11952     for (i = 0; i < MAX_PLAYERS; i++)
11953       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11954
11955     for (i = 0; i < MAX_PLAYERS; i++)
11956       stored_player[i].effective_action = mapped_action[i];
11957
11958 #if DEBUG_PLAYER_ACTIONS
11959     DebugContinued("", "=> ");
11960     for (i = 0; i < MAX_PLAYERS; i++)
11961       DebugContinued("", "%d, ", stored_player[i].effective_action);
11962     DebugContinued("game:playing:player", "\n");
11963 #endif
11964   }
11965 #if DEBUG_PLAYER_ACTIONS
11966   else
11967   {
11968     for (i = 0; i < MAX_PLAYERS; i++)
11969       DebugContinued("", "%d, ", stored_player[i].effective_action);
11970     DebugContinued("game:playing:player", "\n");
11971   }
11972 #endif
11973 #endif
11974
11975   for (i = 0; i < MAX_PLAYERS; i++)
11976   {
11977     // allow engine snapshot in case of changed movement attempt
11978     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11979         (stored_player[i].effective_action & KEY_MOTION))
11980       game.snapshot.changed_action = TRUE;
11981
11982     // allow engine snapshot in case of snapping/dropping attempt
11983     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11984         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11985       game.snapshot.changed_action = TRUE;
11986
11987     game.snapshot.last_action[i] = stored_player[i].effective_action;
11988   }
11989
11990   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11991   {
11992     GameActions_EM_Main();
11993   }
11994   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11995   {
11996     GameActions_SP_Main();
11997   }
11998   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11999   {
12000     GameActions_MM_Main();
12001   }
12002   else
12003   {
12004     GameActions_RND_Main();
12005   }
12006
12007   BlitScreenToBitmap(backbuffer);
12008
12009   CheckLevelSolved();
12010   CheckLevelTime();
12011
12012   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12013
12014   if (global.show_frames_per_second)
12015   {
12016     static unsigned int fps_counter = 0;
12017     static int fps_frames = 0;
12018     unsigned int fps_delay_ms = Counter() - fps_counter;
12019
12020     fps_frames++;
12021
12022     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12023     {
12024       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12025
12026       fps_frames = 0;
12027       fps_counter = Counter();
12028
12029       // always draw FPS to screen after FPS value was updated
12030       redraw_mask |= REDRAW_FPS;
12031     }
12032
12033     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12034     if (GetDrawDeactivationMask() == REDRAW_NONE)
12035       redraw_mask |= REDRAW_FPS;
12036   }
12037 }
12038
12039 static void GameActions_CheckSaveEngineSnapshot(void)
12040 {
12041   if (!game.snapshot.save_snapshot)
12042     return;
12043
12044   // clear flag for saving snapshot _before_ saving snapshot
12045   game.snapshot.save_snapshot = FALSE;
12046
12047   SaveEngineSnapshotToList();
12048 }
12049
12050 void GameActions(void)
12051 {
12052   GameActionsExt();
12053
12054   GameActions_CheckSaveEngineSnapshot();
12055 }
12056
12057 void GameActions_EM_Main(void)
12058 {
12059   byte effective_action[MAX_PLAYERS];
12060   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12061   int i;
12062
12063   for (i = 0; i < MAX_PLAYERS; i++)
12064     effective_action[i] = stored_player[i].effective_action;
12065
12066   GameActions_EM(effective_action, warp_mode);
12067 }
12068
12069 void GameActions_SP_Main(void)
12070 {
12071   byte effective_action[MAX_PLAYERS];
12072   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12073   int i;
12074
12075   for (i = 0; i < MAX_PLAYERS; i++)
12076     effective_action[i] = stored_player[i].effective_action;
12077
12078   GameActions_SP(effective_action, warp_mode);
12079
12080   for (i = 0; i < MAX_PLAYERS; i++)
12081   {
12082     if (stored_player[i].force_dropping)
12083       stored_player[i].action |= KEY_BUTTON_DROP;
12084
12085     stored_player[i].force_dropping = FALSE;
12086   }
12087 }
12088
12089 void GameActions_MM_Main(void)
12090 {
12091   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12092
12093   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12094 }
12095
12096 void GameActions_RND_Main(void)
12097 {
12098   GameActions_RND();
12099 }
12100
12101 void GameActions_RND(void)
12102 {
12103   static struct MouseActionInfo mouse_action_last = { 0 };
12104   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12105   int magic_wall_x = 0, magic_wall_y = 0;
12106   int i, x, y, element, graphic, last_gfx_frame;
12107
12108   InitPlayfieldScanModeVars();
12109
12110   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12111   {
12112     SCAN_PLAYFIELD(x, y)
12113     {
12114       ChangeCount[x][y] = 0;
12115       ChangeEvent[x][y] = -1;
12116     }
12117   }
12118
12119   if (game.set_centered_player)
12120   {
12121     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12122
12123     // switching to "all players" only possible if all players fit to screen
12124     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12125     {
12126       game.centered_player_nr_next = game.centered_player_nr;
12127       game.set_centered_player = FALSE;
12128     }
12129
12130     // do not switch focus to non-existing (or non-active) player
12131     if (game.centered_player_nr_next >= 0 &&
12132         !stored_player[game.centered_player_nr_next].active)
12133     {
12134       game.centered_player_nr_next = game.centered_player_nr;
12135       game.set_centered_player = FALSE;
12136     }
12137   }
12138
12139   if (game.set_centered_player &&
12140       ScreenMovPos == 0)        // screen currently aligned at tile position
12141   {
12142     int sx, sy;
12143
12144     if (game.centered_player_nr_next == -1)
12145     {
12146       setScreenCenteredToAllPlayers(&sx, &sy);
12147     }
12148     else
12149     {
12150       sx = stored_player[game.centered_player_nr_next].jx;
12151       sy = stored_player[game.centered_player_nr_next].jy;
12152     }
12153
12154     game.centered_player_nr = game.centered_player_nr_next;
12155     game.set_centered_player = FALSE;
12156
12157     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12158     DrawGameDoorValues();
12159   }
12160
12161   // check single step mode (set flag and clear again if any player is active)
12162   game.enter_single_step_mode =
12163     (tape.single_step && tape.recording && !tape.pausing);
12164
12165   for (i = 0; i < MAX_PLAYERS; i++)
12166   {
12167     int actual_player_action = stored_player[i].effective_action;
12168
12169 #if 1
12170     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12171        - rnd_equinox_tetrachloride 048
12172        - rnd_equinox_tetrachloride_ii 096
12173        - rnd_emanuel_schmieg 002
12174        - doctor_sloan_ww 001, 020
12175     */
12176     if (stored_player[i].MovPos == 0)
12177       CheckGravityMovement(&stored_player[i]);
12178 #endif
12179
12180     // overwrite programmed action with tape action
12181     if (stored_player[i].programmed_action)
12182       actual_player_action = stored_player[i].programmed_action;
12183
12184     PlayerActions(&stored_player[i], actual_player_action);
12185
12186     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12187   }
12188
12189   // single step pause mode may already have been toggled by "ScrollPlayer()"
12190   if (game.enter_single_step_mode && !tape.pausing)
12191     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12192
12193   ScrollScreen(NULL, SCROLL_GO_ON);
12194
12195   /* for backwards compatibility, the following code emulates a fixed bug that
12196      occured when pushing elements (causing elements that just made their last
12197      pushing step to already (if possible) make their first falling step in the
12198      same game frame, which is bad); this code is also needed to use the famous
12199      "spring push bug" which is used in older levels and might be wanted to be
12200      used also in newer levels, but in this case the buggy pushing code is only
12201      affecting the "spring" element and no other elements */
12202
12203   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12204   {
12205     for (i = 0; i < MAX_PLAYERS; i++)
12206     {
12207       struct PlayerInfo *player = &stored_player[i];
12208       int x = player->jx;
12209       int y = player->jy;
12210
12211       if (player->active && player->is_pushing && player->is_moving &&
12212           IS_MOVING(x, y) &&
12213           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12214            Tile[x][y] == EL_SPRING))
12215       {
12216         ContinueMoving(x, y);
12217
12218         // continue moving after pushing (this is actually a bug)
12219         if (!IS_MOVING(x, y))
12220           Stop[x][y] = FALSE;
12221       }
12222     }
12223   }
12224
12225   SCAN_PLAYFIELD(x, y)
12226   {
12227     Last[x][y] = Tile[x][y];
12228
12229     ChangeCount[x][y] = 0;
12230     ChangeEvent[x][y] = -1;
12231
12232     // this must be handled before main playfield loop
12233     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12234     {
12235       MovDelay[x][y]--;
12236       if (MovDelay[x][y] <= 0)
12237         RemoveField(x, y);
12238     }
12239
12240     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12241     {
12242       MovDelay[x][y]--;
12243       if (MovDelay[x][y] <= 0)
12244       {
12245         int element = Store[x][y];
12246         int move_direction = MovDir[x][y];
12247         int player_index_bit = Store2[x][y];
12248
12249         Store[x][y] = 0;
12250         Store2[x][y] = 0;
12251
12252         RemoveField(x, y);
12253         TEST_DrawLevelField(x, y);
12254
12255         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12256
12257         if (IS_ENVELOPE(element))
12258           local_player->show_envelope = element;
12259       }
12260     }
12261
12262 #if DEBUG
12263     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12264     {
12265       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12266             x, y);
12267       Debug("game:playing:GameActions_RND", "This should never happen!");
12268
12269       ChangePage[x][y] = -1;
12270     }
12271 #endif
12272
12273     Stop[x][y] = FALSE;
12274     if (WasJustMoving[x][y] > 0)
12275       WasJustMoving[x][y]--;
12276     if (WasJustFalling[x][y] > 0)
12277       WasJustFalling[x][y]--;
12278     if (CheckCollision[x][y] > 0)
12279       CheckCollision[x][y]--;
12280     if (CheckImpact[x][y] > 0)
12281       CheckImpact[x][y]--;
12282
12283     GfxFrame[x][y]++;
12284
12285     /* reset finished pushing action (not done in ContinueMoving() to allow
12286        continuous pushing animation for elements with zero push delay) */
12287     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12288     {
12289       ResetGfxAnimation(x, y);
12290       TEST_DrawLevelField(x, y);
12291     }
12292
12293 #if DEBUG
12294     if (IS_BLOCKED(x, y))
12295     {
12296       int oldx, oldy;
12297
12298       Blocked2Moving(x, y, &oldx, &oldy);
12299       if (!IS_MOVING(oldx, oldy))
12300       {
12301         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12302         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12303         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12304         Debug("game:playing:GameActions_RND", "This should never happen!");
12305       }
12306     }
12307 #endif
12308   }
12309
12310   if (mouse_action.button)
12311   {
12312     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12313     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12314
12315     x = mouse_action.lx;
12316     y = mouse_action.ly;
12317     element = Tile[x][y];
12318
12319     if (new_button)
12320     {
12321       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12322       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12323                                          ch_button);
12324     }
12325
12326     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12327     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12328                                        ch_button);
12329
12330     if (level.use_step_counter)
12331     {
12332       boolean counted_click = FALSE;
12333
12334       // element clicked that can change when clicked/pressed
12335       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12336           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12337            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12338         counted_click = TRUE;
12339
12340       // element clicked that can trigger change when clicked/pressed
12341       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12342           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12343         counted_click = TRUE;
12344
12345       if (new_button && counted_click)
12346         CheckLevelTime_StepCounter();
12347     }
12348   }
12349
12350   SCAN_PLAYFIELD(x, y)
12351   {
12352     element = Tile[x][y];
12353     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12354     last_gfx_frame = GfxFrame[x][y];
12355
12356     if (element == EL_EMPTY)
12357       graphic = el2img(GfxElementEmpty[x][y]);
12358
12359     ResetGfxFrame(x, y);
12360
12361     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12362       DrawLevelGraphicAnimation(x, y, graphic);
12363
12364     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12365         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12366       ResetRandomAnimationValue(x, y);
12367
12368     SetRandomAnimationValue(x, y);
12369
12370     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12371
12372     if (IS_INACTIVE(element))
12373     {
12374       if (IS_ANIMATED(graphic))
12375         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12376
12377       continue;
12378     }
12379
12380     // this may take place after moving, so 'element' may have changed
12381     if (IS_CHANGING(x, y) &&
12382         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12383     {
12384       int page = element_info[element].event_page_nr[CE_DELAY];
12385
12386       HandleElementChange(x, y, page);
12387
12388       element = Tile[x][y];
12389       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12390     }
12391
12392     CheckNextToConditions(x, y);
12393
12394     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12395     {
12396       StartMoving(x, y);
12397
12398       element = Tile[x][y];
12399       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12400
12401       if (IS_ANIMATED(graphic) &&
12402           !IS_MOVING(x, y) &&
12403           !Stop[x][y])
12404         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12405
12406       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12407         TEST_DrawTwinkleOnField(x, y);
12408     }
12409     else if (element == EL_ACID)
12410     {
12411       if (!Stop[x][y])
12412         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12413     }
12414     else if ((element == EL_EXIT_OPEN ||
12415               element == EL_EM_EXIT_OPEN ||
12416               element == EL_SP_EXIT_OPEN ||
12417               element == EL_STEEL_EXIT_OPEN ||
12418               element == EL_EM_STEEL_EXIT_OPEN ||
12419               element == EL_SP_TERMINAL ||
12420               element == EL_SP_TERMINAL_ACTIVE ||
12421               element == EL_EXTRA_TIME ||
12422               element == EL_SHIELD_NORMAL ||
12423               element == EL_SHIELD_DEADLY) &&
12424              IS_ANIMATED(graphic))
12425       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12426     else if (IS_MOVING(x, y))
12427       ContinueMoving(x, y);
12428     else if (IS_ACTIVE_BOMB(element))
12429       CheckDynamite(x, y);
12430     else if (element == EL_AMOEBA_GROWING)
12431       AmoebaGrowing(x, y);
12432     else if (element == EL_AMOEBA_SHRINKING)
12433       AmoebaShrinking(x, y);
12434
12435 #if !USE_NEW_AMOEBA_CODE
12436     else if (IS_AMOEBALIVE(element))
12437       AmoebaReproduce(x, y);
12438 #endif
12439
12440     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12441       Life(x, y);
12442     else if (element == EL_EXIT_CLOSED)
12443       CheckExit(x, y);
12444     else if (element == EL_EM_EXIT_CLOSED)
12445       CheckExitEM(x, y);
12446     else if (element == EL_STEEL_EXIT_CLOSED)
12447       CheckExitSteel(x, y);
12448     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12449       CheckExitSteelEM(x, y);
12450     else if (element == EL_SP_EXIT_CLOSED)
12451       CheckExitSP(x, y);
12452     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12453              element == EL_EXPANDABLE_STEELWALL_GROWING)
12454       MauerWaechst(x, y);
12455     else if (element == EL_EXPANDABLE_WALL ||
12456              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12457              element == EL_EXPANDABLE_WALL_VERTICAL ||
12458              element == EL_EXPANDABLE_WALL_ANY ||
12459              element == EL_BD_EXPANDABLE_WALL)
12460       MauerAbleger(x, y);
12461     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12462              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12463              element == EL_EXPANDABLE_STEELWALL_ANY)
12464       MauerAblegerStahl(x, y);
12465     else if (element == EL_FLAMES)
12466       CheckForDragon(x, y);
12467     else if (element == EL_EXPLOSION)
12468       ; // drawing of correct explosion animation is handled separately
12469     else if (element == EL_ELEMENT_SNAPPING ||
12470              element == EL_DIAGONAL_SHRINKING ||
12471              element == EL_DIAGONAL_GROWING)
12472     {
12473       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12474
12475       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12476     }
12477     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12478       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12479
12480     if (IS_BELT_ACTIVE(element))
12481       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12482
12483     if (game.magic_wall_active)
12484     {
12485       int jx = local_player->jx, jy = local_player->jy;
12486
12487       // play the element sound at the position nearest to the player
12488       if ((element == EL_MAGIC_WALL_FULL ||
12489            element == EL_MAGIC_WALL_ACTIVE ||
12490            element == EL_MAGIC_WALL_EMPTYING ||
12491            element == EL_BD_MAGIC_WALL_FULL ||
12492            element == EL_BD_MAGIC_WALL_ACTIVE ||
12493            element == EL_BD_MAGIC_WALL_EMPTYING ||
12494            element == EL_DC_MAGIC_WALL_FULL ||
12495            element == EL_DC_MAGIC_WALL_ACTIVE ||
12496            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12497           ABS(x - jx) + ABS(y - jy) <
12498           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12499       {
12500         magic_wall_x = x;
12501         magic_wall_y = y;
12502       }
12503     }
12504   }
12505
12506 #if USE_NEW_AMOEBA_CODE
12507   // new experimental amoeba growth stuff
12508   if (!(FrameCounter % 8))
12509   {
12510     static unsigned int random = 1684108901;
12511
12512     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12513     {
12514       x = RND(lev_fieldx);
12515       y = RND(lev_fieldy);
12516       element = Tile[x][y];
12517
12518       if (!IS_PLAYER(x,y) &&
12519           (element == EL_EMPTY ||
12520            CAN_GROW_INTO(element) ||
12521            element == EL_QUICKSAND_EMPTY ||
12522            element == EL_QUICKSAND_FAST_EMPTY ||
12523            element == EL_ACID_SPLASH_LEFT ||
12524            element == EL_ACID_SPLASH_RIGHT))
12525       {
12526         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12527             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12528             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12529             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12530           Tile[x][y] = EL_AMOEBA_DROP;
12531       }
12532
12533       random = random * 129 + 1;
12534     }
12535   }
12536 #endif
12537
12538   game.explosions_delayed = FALSE;
12539
12540   SCAN_PLAYFIELD(x, y)
12541   {
12542     element = Tile[x][y];
12543
12544     if (ExplodeField[x][y])
12545       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12546     else if (element == EL_EXPLOSION)
12547       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12548
12549     ExplodeField[x][y] = EX_TYPE_NONE;
12550   }
12551
12552   game.explosions_delayed = TRUE;
12553
12554   if (game.magic_wall_active)
12555   {
12556     if (!(game.magic_wall_time_left % 4))
12557     {
12558       int element = Tile[magic_wall_x][magic_wall_y];
12559
12560       if (element == EL_BD_MAGIC_WALL_FULL ||
12561           element == EL_BD_MAGIC_WALL_ACTIVE ||
12562           element == EL_BD_MAGIC_WALL_EMPTYING)
12563         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12564       else if (element == EL_DC_MAGIC_WALL_FULL ||
12565                element == EL_DC_MAGIC_WALL_ACTIVE ||
12566                element == EL_DC_MAGIC_WALL_EMPTYING)
12567         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12568       else
12569         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12570     }
12571
12572     if (game.magic_wall_time_left > 0)
12573     {
12574       game.magic_wall_time_left--;
12575
12576       if (!game.magic_wall_time_left)
12577       {
12578         SCAN_PLAYFIELD(x, y)
12579         {
12580           element = Tile[x][y];
12581
12582           if (element == EL_MAGIC_WALL_ACTIVE ||
12583               element == EL_MAGIC_WALL_FULL)
12584           {
12585             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12586             TEST_DrawLevelField(x, y);
12587           }
12588           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12589                    element == EL_BD_MAGIC_WALL_FULL)
12590           {
12591             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12592             TEST_DrawLevelField(x, y);
12593           }
12594           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12595                    element == EL_DC_MAGIC_WALL_FULL)
12596           {
12597             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12598             TEST_DrawLevelField(x, y);
12599           }
12600         }
12601
12602         game.magic_wall_active = FALSE;
12603       }
12604     }
12605   }
12606
12607   if (game.light_time_left > 0)
12608   {
12609     game.light_time_left--;
12610
12611     if (game.light_time_left == 0)
12612       RedrawAllLightSwitchesAndInvisibleElements();
12613   }
12614
12615   if (game.timegate_time_left > 0)
12616   {
12617     game.timegate_time_left--;
12618
12619     if (game.timegate_time_left == 0)
12620       CloseAllOpenTimegates();
12621   }
12622
12623   if (game.lenses_time_left > 0)
12624   {
12625     game.lenses_time_left--;
12626
12627     if (game.lenses_time_left == 0)
12628       RedrawAllInvisibleElementsForLenses();
12629   }
12630
12631   if (game.magnify_time_left > 0)
12632   {
12633     game.magnify_time_left--;
12634
12635     if (game.magnify_time_left == 0)
12636       RedrawAllInvisibleElementsForMagnifier();
12637   }
12638
12639   for (i = 0; i < MAX_PLAYERS; i++)
12640   {
12641     struct PlayerInfo *player = &stored_player[i];
12642
12643     if (SHIELD_ON(player))
12644     {
12645       if (player->shield_deadly_time_left)
12646         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12647       else if (player->shield_normal_time_left)
12648         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12649     }
12650   }
12651
12652 #if USE_DELAYED_GFX_REDRAW
12653   SCAN_PLAYFIELD(x, y)
12654   {
12655     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12656     {
12657       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12658          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12659
12660       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12661         DrawLevelField(x, y);
12662
12663       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12664         DrawLevelFieldCrumbled(x, y);
12665
12666       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12667         DrawLevelFieldCrumbledNeighbours(x, y);
12668
12669       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12670         DrawTwinkleOnField(x, y);
12671     }
12672
12673     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12674   }
12675 #endif
12676
12677   DrawAllPlayers();
12678   PlayAllPlayersSound();
12679
12680   for (i = 0; i < MAX_PLAYERS; i++)
12681   {
12682     struct PlayerInfo *player = &stored_player[i];
12683
12684     if (player->show_envelope != 0 && (!player->active ||
12685                                        player->MovPos == 0))
12686     {
12687       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12688
12689       player->show_envelope = 0;
12690     }
12691   }
12692
12693   // use random number generator in every frame to make it less predictable
12694   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12695     RND(1);
12696
12697   mouse_action_last = mouse_action;
12698 }
12699
12700 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12701 {
12702   int min_x = x, min_y = y, max_x = x, max_y = y;
12703   int scr_fieldx = getScreenFieldSizeX();
12704   int scr_fieldy = getScreenFieldSizeY();
12705   int i;
12706
12707   for (i = 0; i < MAX_PLAYERS; i++)
12708   {
12709     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12710
12711     if (!stored_player[i].active || &stored_player[i] == player)
12712       continue;
12713
12714     min_x = MIN(min_x, jx);
12715     min_y = MIN(min_y, jy);
12716     max_x = MAX(max_x, jx);
12717     max_y = MAX(max_y, jy);
12718   }
12719
12720   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12721 }
12722
12723 static boolean AllPlayersInVisibleScreen(void)
12724 {
12725   int i;
12726
12727   for (i = 0; i < MAX_PLAYERS; i++)
12728   {
12729     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12730
12731     if (!stored_player[i].active)
12732       continue;
12733
12734     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12735       return FALSE;
12736   }
12737
12738   return TRUE;
12739 }
12740
12741 void ScrollLevel(int dx, int dy)
12742 {
12743   int scroll_offset = 2 * TILEX_VAR;
12744   int x, y;
12745
12746   BlitBitmap(drawto_field, drawto_field,
12747              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12748              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12749              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12750              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12751              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12752              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12753
12754   if (dx != 0)
12755   {
12756     x = (dx == 1 ? BX1 : BX2);
12757     for (y = BY1; y <= BY2; y++)
12758       DrawScreenField(x, y);
12759   }
12760
12761   if (dy != 0)
12762   {
12763     y = (dy == 1 ? BY1 : BY2);
12764     for (x = BX1; x <= BX2; x++)
12765       DrawScreenField(x, y);
12766   }
12767
12768   redraw_mask |= REDRAW_FIELD;
12769 }
12770
12771 static boolean canFallDown(struct PlayerInfo *player)
12772 {
12773   int jx = player->jx, jy = player->jy;
12774
12775   return (IN_LEV_FIELD(jx, jy + 1) &&
12776           (IS_FREE(jx, jy + 1) ||
12777            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12778           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12779           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12780 }
12781
12782 static boolean canPassField(int x, int y, int move_dir)
12783 {
12784   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12785   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12786   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12787   int nextx = x + dx;
12788   int nexty = y + dy;
12789   int element = Tile[x][y];
12790
12791   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12792           !CAN_MOVE(element) &&
12793           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12794           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12795           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12796 }
12797
12798 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12799 {
12800   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12801   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12802   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12803   int newx = x + dx;
12804   int newy = y + dy;
12805
12806   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12807           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12808           (IS_DIGGABLE(Tile[newx][newy]) ||
12809            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12810            canPassField(newx, newy, move_dir)));
12811 }
12812
12813 static void CheckGravityMovement(struct PlayerInfo *player)
12814 {
12815   if (player->gravity && !player->programmed_action)
12816   {
12817     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12818     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12819     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12820     int jx = player->jx, jy = player->jy;
12821     boolean player_is_moving_to_valid_field =
12822       (!player_is_snapping &&
12823        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12824         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12825     boolean player_can_fall_down = canFallDown(player);
12826
12827     if (player_can_fall_down &&
12828         !player_is_moving_to_valid_field)
12829       player->programmed_action = MV_DOWN;
12830   }
12831 }
12832
12833 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12834 {
12835   return CheckGravityMovement(player);
12836
12837   if (player->gravity && !player->programmed_action)
12838   {
12839     int jx = player->jx, jy = player->jy;
12840     boolean field_under_player_is_free =
12841       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12842     boolean player_is_standing_on_valid_field =
12843       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12844        (IS_WALKABLE(Tile[jx][jy]) &&
12845         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12846
12847     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12848       player->programmed_action = MV_DOWN;
12849   }
12850 }
12851
12852 /*
12853   MovePlayerOneStep()
12854   -----------------------------------------------------------------------------
12855   dx, dy:               direction (non-diagonal) to try to move the player to
12856   real_dx, real_dy:     direction as read from input device (can be diagonal)
12857 */
12858
12859 boolean MovePlayerOneStep(struct PlayerInfo *player,
12860                           int dx, int dy, int real_dx, int real_dy)
12861 {
12862   int jx = player->jx, jy = player->jy;
12863   int new_jx = jx + dx, new_jy = jy + dy;
12864   int can_move;
12865   boolean player_can_move = !player->cannot_move;
12866
12867   if (!player->active || (!dx && !dy))
12868     return MP_NO_ACTION;
12869
12870   player->MovDir = (dx < 0 ? MV_LEFT :
12871                     dx > 0 ? MV_RIGHT :
12872                     dy < 0 ? MV_UP :
12873                     dy > 0 ? MV_DOWN :  MV_NONE);
12874
12875   if (!IN_LEV_FIELD(new_jx, new_jy))
12876     return MP_NO_ACTION;
12877
12878   if (!player_can_move)
12879   {
12880     if (player->MovPos == 0)
12881     {
12882       player->is_moving = FALSE;
12883       player->is_digging = FALSE;
12884       player->is_collecting = FALSE;
12885       player->is_snapping = FALSE;
12886       player->is_pushing = FALSE;
12887     }
12888   }
12889
12890   if (!network.enabled && game.centered_player_nr == -1 &&
12891       !AllPlayersInSight(player, new_jx, new_jy))
12892     return MP_NO_ACTION;
12893
12894   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12895   if (can_move != MP_MOVING)
12896     return can_move;
12897
12898   // check if DigField() has caused relocation of the player
12899   if (player->jx != jx || player->jy != jy)
12900     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12901
12902   StorePlayer[jx][jy] = 0;
12903   player->last_jx = jx;
12904   player->last_jy = jy;
12905   player->jx = new_jx;
12906   player->jy = new_jy;
12907   StorePlayer[new_jx][new_jy] = player->element_nr;
12908
12909   if (player->move_delay_value_next != -1)
12910   {
12911     player->move_delay_value = player->move_delay_value_next;
12912     player->move_delay_value_next = -1;
12913   }
12914
12915   player->MovPos =
12916     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12917
12918   player->step_counter++;
12919
12920   PlayerVisit[jx][jy] = FrameCounter;
12921
12922   player->is_moving = TRUE;
12923
12924 #if 1
12925   // should better be called in MovePlayer(), but this breaks some tapes
12926   ScrollPlayer(player, SCROLL_INIT);
12927 #endif
12928
12929   return MP_MOVING;
12930 }
12931
12932 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12933 {
12934   int jx = player->jx, jy = player->jy;
12935   int old_jx = jx, old_jy = jy;
12936   int moved = MP_NO_ACTION;
12937
12938   if (!player->active)
12939     return FALSE;
12940
12941   if (!dx && !dy)
12942   {
12943     if (player->MovPos == 0)
12944     {
12945       player->is_moving = FALSE;
12946       player->is_digging = FALSE;
12947       player->is_collecting = FALSE;
12948       player->is_snapping = FALSE;
12949       player->is_pushing = FALSE;
12950     }
12951
12952     return FALSE;
12953   }
12954
12955   if (player->move_delay > 0)
12956     return FALSE;
12957
12958   player->move_delay = -1;              // set to "uninitialized" value
12959
12960   // store if player is automatically moved to next field
12961   player->is_auto_moving = (player->programmed_action != MV_NONE);
12962
12963   // remove the last programmed player action
12964   player->programmed_action = 0;
12965
12966   if (player->MovPos)
12967   {
12968     // should only happen if pre-1.2 tape recordings are played
12969     // this is only for backward compatibility
12970
12971     int original_move_delay_value = player->move_delay_value;
12972
12973 #if DEBUG
12974     Debug("game:playing:MovePlayer",
12975           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12976           tape.counter);
12977 #endif
12978
12979     // scroll remaining steps with finest movement resolution
12980     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12981
12982     while (player->MovPos)
12983     {
12984       ScrollPlayer(player, SCROLL_GO_ON);
12985       ScrollScreen(NULL, SCROLL_GO_ON);
12986
12987       AdvanceFrameAndPlayerCounters(player->index_nr);
12988
12989       DrawAllPlayers();
12990       BackToFront_WithFrameDelay(0);
12991     }
12992
12993     player->move_delay_value = original_move_delay_value;
12994   }
12995
12996   player->is_active = FALSE;
12997
12998   if (player->last_move_dir & MV_HORIZONTAL)
12999   {
13000     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13001       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13002   }
13003   else
13004   {
13005     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13006       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13007   }
13008
13009   if (!moved && !player->is_active)
13010   {
13011     player->is_moving = FALSE;
13012     player->is_digging = FALSE;
13013     player->is_collecting = FALSE;
13014     player->is_snapping = FALSE;
13015     player->is_pushing = FALSE;
13016   }
13017
13018   jx = player->jx;
13019   jy = player->jy;
13020
13021   if (moved & MP_MOVING && !ScreenMovPos &&
13022       (player->index_nr == game.centered_player_nr ||
13023        game.centered_player_nr == -1))
13024   {
13025     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13026
13027     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13028     {
13029       // actual player has left the screen -- scroll in that direction
13030       if (jx != old_jx)         // player has moved horizontally
13031         scroll_x += (jx - old_jx);
13032       else                      // player has moved vertically
13033         scroll_y += (jy - old_jy);
13034     }
13035     else
13036     {
13037       int offset_raw = game.scroll_delay_value;
13038
13039       if (jx != old_jx)         // player has moved horizontally
13040       {
13041         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13042         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13043         int new_scroll_x = jx - MIDPOSX + offset_x;
13044
13045         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13046             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13047           scroll_x = new_scroll_x;
13048
13049         // don't scroll over playfield boundaries
13050         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13051
13052         // don't scroll more than one field at a time
13053         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13054
13055         // don't scroll against the player's moving direction
13056         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13057             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13058           scroll_x = old_scroll_x;
13059       }
13060       else                      // player has moved vertically
13061       {
13062         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13063         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13064         int new_scroll_y = jy - MIDPOSY + offset_y;
13065
13066         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13067             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13068           scroll_y = new_scroll_y;
13069
13070         // don't scroll over playfield boundaries
13071         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13072
13073         // don't scroll more than one field at a time
13074         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13075
13076         // don't scroll against the player's moving direction
13077         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13078             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13079           scroll_y = old_scroll_y;
13080       }
13081     }
13082
13083     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13084     {
13085       if (!network.enabled && game.centered_player_nr == -1 &&
13086           !AllPlayersInVisibleScreen())
13087       {
13088         scroll_x = old_scroll_x;
13089         scroll_y = old_scroll_y;
13090       }
13091       else
13092       {
13093         ScrollScreen(player, SCROLL_INIT);
13094         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13095       }
13096     }
13097   }
13098
13099   player->StepFrame = 0;
13100
13101   if (moved & MP_MOVING)
13102   {
13103     if (old_jx != jx && old_jy == jy)
13104       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13105     else if (old_jx == jx && old_jy != jy)
13106       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13107
13108     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13109
13110     player->last_move_dir = player->MovDir;
13111     player->is_moving = TRUE;
13112     player->is_snapping = FALSE;
13113     player->is_switching = FALSE;
13114     player->is_dropping = FALSE;
13115     player->is_dropping_pressed = FALSE;
13116     player->drop_pressed_delay = 0;
13117
13118 #if 0
13119     // should better be called here than above, but this breaks some tapes
13120     ScrollPlayer(player, SCROLL_INIT);
13121 #endif
13122   }
13123   else
13124   {
13125     CheckGravityMovementWhenNotMoving(player);
13126
13127     player->is_moving = FALSE;
13128
13129     /* at this point, the player is allowed to move, but cannot move right now
13130        (e.g. because of something blocking the way) -- ensure that the player
13131        is also allowed to move in the next frame (in old versions before 3.1.1,
13132        the player was forced to wait again for eight frames before next try) */
13133
13134     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13135       player->move_delay = 0;   // allow direct movement in the next frame
13136   }
13137
13138   if (player->move_delay == -1)         // not yet initialized by DigField()
13139     player->move_delay = player->move_delay_value;
13140
13141   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13142   {
13143     TestIfPlayerTouchesBadThing(jx, jy);
13144     TestIfPlayerTouchesCustomElement(jx, jy);
13145   }
13146
13147   if (!player->active)
13148     RemovePlayer(player);
13149
13150   return moved;
13151 }
13152
13153 void ScrollPlayer(struct PlayerInfo *player, int mode)
13154 {
13155   int jx = player->jx, jy = player->jy;
13156   int last_jx = player->last_jx, last_jy = player->last_jy;
13157   int move_stepsize = TILEX / player->move_delay_value;
13158
13159   if (!player->active)
13160     return;
13161
13162   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13163     return;
13164
13165   if (mode == SCROLL_INIT)
13166   {
13167     player->actual_frame_counter = FrameCounter;
13168     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13169
13170     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13171         Tile[last_jx][last_jy] == EL_EMPTY)
13172     {
13173       int last_field_block_delay = 0;   // start with no blocking at all
13174       int block_delay_adjustment = player->block_delay_adjustment;
13175
13176       // if player blocks last field, add delay for exactly one move
13177       if (player->block_last_field)
13178       {
13179         last_field_block_delay += player->move_delay_value;
13180
13181         // when blocking enabled, prevent moving up despite gravity
13182         if (player->gravity && player->MovDir == MV_UP)
13183           block_delay_adjustment = -1;
13184       }
13185
13186       // add block delay adjustment (also possible when not blocking)
13187       last_field_block_delay += block_delay_adjustment;
13188
13189       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13190       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13191     }
13192
13193     if (player->MovPos != 0)    // player has not yet reached destination
13194       return;
13195   }
13196   else if (!FrameReached(&player->actual_frame_counter, 1))
13197     return;
13198
13199   if (player->MovPos != 0)
13200   {
13201     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13202     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13203
13204     // before DrawPlayer() to draw correct player graphic for this case
13205     if (player->MovPos == 0)
13206       CheckGravityMovement(player);
13207   }
13208
13209   if (player->MovPos == 0)      // player reached destination field
13210   {
13211     if (player->move_delay_reset_counter > 0)
13212     {
13213       player->move_delay_reset_counter--;
13214
13215       if (player->move_delay_reset_counter == 0)
13216       {
13217         // continue with normal speed after quickly moving through gate
13218         HALVE_PLAYER_SPEED(player);
13219
13220         // be able to make the next move without delay
13221         player->move_delay = 0;
13222       }
13223     }
13224
13225     player->last_jx = jx;
13226     player->last_jy = jy;
13227
13228     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13229         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13230         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13231         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13232         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13233         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13234         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13235         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13236     {
13237       ExitPlayer(player);
13238
13239       if (game.players_still_needed == 0 &&
13240           (game.friends_still_needed == 0 ||
13241            IS_SP_ELEMENT(Tile[jx][jy])))
13242         LevelSolved();
13243     }
13244
13245     // this breaks one level: "machine", level 000
13246     {
13247       int move_direction = player->MovDir;
13248       int enter_side = MV_DIR_OPPOSITE(move_direction);
13249       int leave_side = move_direction;
13250       int old_jx = last_jx;
13251       int old_jy = last_jy;
13252       int old_element = Tile[old_jx][old_jy];
13253       int new_element = Tile[jx][jy];
13254
13255       if (IS_CUSTOM_ELEMENT(old_element))
13256         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13257                                    CE_LEFT_BY_PLAYER,
13258                                    player->index_bit, leave_side);
13259
13260       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13261                                           CE_PLAYER_LEAVES_X,
13262                                           player->index_bit, leave_side);
13263
13264       if (IS_CUSTOM_ELEMENT(new_element))
13265         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13266                                    player->index_bit, enter_side);
13267
13268       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13269                                           CE_PLAYER_ENTERS_X,
13270                                           player->index_bit, enter_side);
13271
13272       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13273                                         CE_MOVE_OF_X, move_direction);
13274     }
13275
13276     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13277     {
13278       TestIfPlayerTouchesBadThing(jx, jy);
13279       TestIfPlayerTouchesCustomElement(jx, jy);
13280
13281       /* needed because pushed element has not yet reached its destination,
13282          so it would trigger a change event at its previous field location */
13283       if (!player->is_pushing)
13284         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13285
13286       if (level.finish_dig_collect &&
13287           (player->is_digging || player->is_collecting))
13288       {
13289         int last_element = player->last_removed_element;
13290         int move_direction = player->MovDir;
13291         int enter_side = MV_DIR_OPPOSITE(move_direction);
13292         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13293                             CE_PLAYER_COLLECTS_X);
13294
13295         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13296                                             player->index_bit, enter_side);
13297
13298         player->last_removed_element = EL_UNDEFINED;
13299       }
13300
13301       if (!player->active)
13302         RemovePlayer(player);
13303     }
13304
13305     if (level.use_step_counter)
13306       CheckLevelTime_StepCounter();
13307
13308     if (tape.single_step && tape.recording && !tape.pausing &&
13309         !player->programmed_action)
13310       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13311
13312     if (!player->programmed_action)
13313       CheckSaveEngineSnapshot(player);
13314   }
13315 }
13316
13317 void ScrollScreen(struct PlayerInfo *player, int mode)
13318 {
13319   static unsigned int screen_frame_counter = 0;
13320
13321   if (mode == SCROLL_INIT)
13322   {
13323     // set scrolling step size according to actual player's moving speed
13324     ScrollStepSize = TILEX / player->move_delay_value;
13325
13326     screen_frame_counter = FrameCounter;
13327     ScreenMovDir = player->MovDir;
13328     ScreenMovPos = player->MovPos;
13329     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13330     return;
13331   }
13332   else if (!FrameReached(&screen_frame_counter, 1))
13333     return;
13334
13335   if (ScreenMovPos)
13336   {
13337     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13338     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13339     redraw_mask |= REDRAW_FIELD;
13340   }
13341   else
13342     ScreenMovDir = MV_NONE;
13343 }
13344
13345 void CheckNextToConditions(int x, int y)
13346 {
13347   int element = Tile[x][y];
13348
13349   if (IS_PLAYER(x, y))
13350     TestIfPlayerNextToCustomElement(x, y);
13351
13352   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13353       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13354     TestIfElementNextToCustomElement(x, y);
13355 }
13356
13357 void TestIfPlayerNextToCustomElement(int x, int y)
13358 {
13359   static int xy[4][2] =
13360   {
13361     { 0, -1 },
13362     { -1, 0 },
13363     { +1, 0 },
13364     { 0, +1 }
13365   };
13366   static int trigger_sides[4][2] =
13367   {
13368     // center side       border side
13369     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13370     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13371     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13372     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13373   };
13374   int i;
13375
13376   if (!IS_PLAYER(x, y))
13377     return;
13378
13379   struct PlayerInfo *player = PLAYERINFO(x, y);
13380
13381   if (player->is_moving)
13382     return;
13383
13384   for (i = 0; i < NUM_DIRECTIONS; i++)
13385   {
13386     int xx = x + xy[i][0];
13387     int yy = y + xy[i][1];
13388     int border_side = trigger_sides[i][1];
13389     int border_element;
13390
13391     if (!IN_LEV_FIELD(xx, yy))
13392       continue;
13393
13394     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13395       continue;         // center and border element not connected
13396
13397     border_element = Tile[xx][yy];
13398
13399     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13400                                player->index_bit, border_side);
13401     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13402                                         CE_PLAYER_NEXT_TO_X,
13403                                         player->index_bit, border_side);
13404
13405     /* use player element that is initially defined in the level playfield,
13406        not the player element that corresponds to the runtime player number
13407        (example: a level that contains EL_PLAYER_3 as the only player would
13408        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13409
13410     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13411                              CE_NEXT_TO_X, border_side);
13412   }
13413 }
13414
13415 void TestIfPlayerTouchesCustomElement(int x, int y)
13416 {
13417   static int xy[4][2] =
13418   {
13419     { 0, -1 },
13420     { -1, 0 },
13421     { +1, 0 },
13422     { 0, +1 }
13423   };
13424   static int trigger_sides[4][2] =
13425   {
13426     // center side       border side
13427     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13428     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13429     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13430     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13431   };
13432   static int touch_dir[4] =
13433   {
13434     MV_LEFT | MV_RIGHT,
13435     MV_UP   | MV_DOWN,
13436     MV_UP   | MV_DOWN,
13437     MV_LEFT | MV_RIGHT
13438   };
13439   int center_element = Tile[x][y];      // should always be non-moving!
13440   int i;
13441
13442   for (i = 0; i < NUM_DIRECTIONS; i++)
13443   {
13444     int xx = x + xy[i][0];
13445     int yy = y + xy[i][1];
13446     int center_side = trigger_sides[i][0];
13447     int border_side = trigger_sides[i][1];
13448     int border_element;
13449
13450     if (!IN_LEV_FIELD(xx, yy))
13451       continue;
13452
13453     if (IS_PLAYER(x, y))                // player found at center element
13454     {
13455       struct PlayerInfo *player = PLAYERINFO(x, y);
13456
13457       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13458         border_element = Tile[xx][yy];          // may be moving!
13459       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13460         border_element = Tile[xx][yy];
13461       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13462         border_element = MovingOrBlocked2Element(xx, yy);
13463       else
13464         continue;               // center and border element do not touch
13465
13466       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13467                                  player->index_bit, border_side);
13468       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13469                                           CE_PLAYER_TOUCHES_X,
13470                                           player->index_bit, border_side);
13471
13472       {
13473         /* use player element that is initially defined in the level playfield,
13474            not the player element that corresponds to the runtime player number
13475            (example: a level that contains EL_PLAYER_3 as the only player would
13476            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13477         int player_element = PLAYERINFO(x, y)->initial_element;
13478
13479         CheckElementChangeBySide(xx, yy, border_element, player_element,
13480                                  CE_TOUCHING_X, border_side);
13481       }
13482     }
13483     else if (IS_PLAYER(xx, yy))         // player found at border element
13484     {
13485       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13486
13487       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13488       {
13489         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13490           continue;             // center and border element do not touch
13491       }
13492
13493       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13494                                  player->index_bit, center_side);
13495       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13496                                           CE_PLAYER_TOUCHES_X,
13497                                           player->index_bit, center_side);
13498
13499       {
13500         /* use player element that is initially defined in the level playfield,
13501            not the player element that corresponds to the runtime player number
13502            (example: a level that contains EL_PLAYER_3 as the only player would
13503            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13504         int player_element = PLAYERINFO(xx, yy)->initial_element;
13505
13506         CheckElementChangeBySide(x, y, center_element, player_element,
13507                                  CE_TOUCHING_X, center_side);
13508       }
13509
13510       break;
13511     }
13512   }
13513 }
13514
13515 void TestIfElementNextToCustomElement(int x, int y)
13516 {
13517   static int xy[4][2] =
13518   {
13519     { 0, -1 },
13520     { -1, 0 },
13521     { +1, 0 },
13522     { 0, +1 }
13523   };
13524   static int trigger_sides[4][2] =
13525   {
13526     // center side      border side
13527     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13528     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13529     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13530     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13531   };
13532   int center_element = Tile[x][y];      // should always be non-moving!
13533   int i;
13534
13535   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13536     return;
13537
13538   for (i = 0; i < NUM_DIRECTIONS; i++)
13539   {
13540     int xx = x + xy[i][0];
13541     int yy = y + xy[i][1];
13542     int border_side = trigger_sides[i][1];
13543     int border_element;
13544
13545     if (!IN_LEV_FIELD(xx, yy))
13546       continue;
13547
13548     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13549       continue;                 // center and border element not connected
13550
13551     border_element = Tile[xx][yy];
13552
13553     // check for change of center element (but change it only once)
13554     if (CheckElementChangeBySide(x, y, center_element, border_element,
13555                                  CE_NEXT_TO_X, border_side))
13556       break;
13557   }
13558 }
13559
13560 void TestIfElementTouchesCustomElement(int x, int y)
13561 {
13562   static int xy[4][2] =
13563   {
13564     { 0, -1 },
13565     { -1, 0 },
13566     { +1, 0 },
13567     { 0, +1 }
13568   };
13569   static int trigger_sides[4][2] =
13570   {
13571     // center side      border side
13572     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13573     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13574     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13575     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13576   };
13577   static int touch_dir[4] =
13578   {
13579     MV_LEFT | MV_RIGHT,
13580     MV_UP   | MV_DOWN,
13581     MV_UP   | MV_DOWN,
13582     MV_LEFT | MV_RIGHT
13583   };
13584   boolean change_center_element = FALSE;
13585   int center_element = Tile[x][y];      // should always be non-moving!
13586   int border_element_old[NUM_DIRECTIONS];
13587   int i;
13588
13589   for (i = 0; i < NUM_DIRECTIONS; i++)
13590   {
13591     int xx = x + xy[i][0];
13592     int yy = y + xy[i][1];
13593     int border_element;
13594
13595     border_element_old[i] = -1;
13596
13597     if (!IN_LEV_FIELD(xx, yy))
13598       continue;
13599
13600     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13601       border_element = Tile[xx][yy];    // may be moving!
13602     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13603       border_element = Tile[xx][yy];
13604     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13605       border_element = MovingOrBlocked2Element(xx, yy);
13606     else
13607       continue;                 // center and border element do not touch
13608
13609     border_element_old[i] = border_element;
13610   }
13611
13612   for (i = 0; i < NUM_DIRECTIONS; i++)
13613   {
13614     int xx = x + xy[i][0];
13615     int yy = y + xy[i][1];
13616     int center_side = trigger_sides[i][0];
13617     int border_element = border_element_old[i];
13618
13619     if (border_element == -1)
13620       continue;
13621
13622     // check for change of border element
13623     CheckElementChangeBySide(xx, yy, border_element, center_element,
13624                              CE_TOUCHING_X, center_side);
13625
13626     // (center element cannot be player, so we dont have to check this here)
13627   }
13628
13629   for (i = 0; i < NUM_DIRECTIONS; i++)
13630   {
13631     int xx = x + xy[i][0];
13632     int yy = y + xy[i][1];
13633     int border_side = trigger_sides[i][1];
13634     int border_element = border_element_old[i];
13635
13636     if (border_element == -1)
13637       continue;
13638
13639     // check for change of center element (but change it only once)
13640     if (!change_center_element)
13641       change_center_element =
13642         CheckElementChangeBySide(x, y, center_element, border_element,
13643                                  CE_TOUCHING_X, border_side);
13644
13645     if (IS_PLAYER(xx, yy))
13646     {
13647       /* use player element that is initially defined in the level playfield,
13648          not the player element that corresponds to the runtime player number
13649          (example: a level that contains EL_PLAYER_3 as the only player would
13650          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13651       int player_element = PLAYERINFO(xx, yy)->initial_element;
13652
13653       CheckElementChangeBySide(x, y, center_element, player_element,
13654                                CE_TOUCHING_X, border_side);
13655     }
13656   }
13657 }
13658
13659 void TestIfElementHitsCustomElement(int x, int y, int direction)
13660 {
13661   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13662   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13663   int hitx = x + dx, hity = y + dy;
13664   int hitting_element = Tile[x][y];
13665   int touched_element;
13666
13667   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13668     return;
13669
13670   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13671                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13672
13673   if (IN_LEV_FIELD(hitx, hity))
13674   {
13675     int opposite_direction = MV_DIR_OPPOSITE(direction);
13676     int hitting_side = direction;
13677     int touched_side = opposite_direction;
13678     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13679                           MovDir[hitx][hity] != direction ||
13680                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13681
13682     object_hit = TRUE;
13683
13684     if (object_hit)
13685     {
13686       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13687                                CE_HITTING_X, touched_side);
13688
13689       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13690                                CE_HIT_BY_X, hitting_side);
13691
13692       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13693                                CE_HIT_BY_SOMETHING, opposite_direction);
13694
13695       if (IS_PLAYER(hitx, hity))
13696       {
13697         /* use player element that is initially defined in the level playfield,
13698            not the player element that corresponds to the runtime player number
13699            (example: a level that contains EL_PLAYER_3 as the only player would
13700            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13701         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13702
13703         CheckElementChangeBySide(x, y, hitting_element, player_element,
13704                                  CE_HITTING_X, touched_side);
13705       }
13706     }
13707   }
13708
13709   // "hitting something" is also true when hitting the playfield border
13710   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13711                            CE_HITTING_SOMETHING, direction);
13712 }
13713
13714 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13715 {
13716   int i, kill_x = -1, kill_y = -1;
13717
13718   int bad_element = -1;
13719   static int test_xy[4][2] =
13720   {
13721     { 0, -1 },
13722     { -1, 0 },
13723     { +1, 0 },
13724     { 0, +1 }
13725   };
13726   static int test_dir[4] =
13727   {
13728     MV_UP,
13729     MV_LEFT,
13730     MV_RIGHT,
13731     MV_DOWN
13732   };
13733
13734   for (i = 0; i < NUM_DIRECTIONS; i++)
13735   {
13736     int test_x, test_y, test_move_dir, test_element;
13737
13738     test_x = good_x + test_xy[i][0];
13739     test_y = good_y + test_xy[i][1];
13740
13741     if (!IN_LEV_FIELD(test_x, test_y))
13742       continue;
13743
13744     test_move_dir =
13745       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13746
13747     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13748
13749     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13750        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13751     */
13752     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13753         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13754     {
13755       kill_x = test_x;
13756       kill_y = test_y;
13757       bad_element = test_element;
13758
13759       break;
13760     }
13761   }
13762
13763   if (kill_x != -1 || kill_y != -1)
13764   {
13765     if (IS_PLAYER(good_x, good_y))
13766     {
13767       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13768
13769       if (player->shield_deadly_time_left > 0 &&
13770           !IS_INDESTRUCTIBLE(bad_element))
13771         Bang(kill_x, kill_y);
13772       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13773         KillPlayer(player);
13774     }
13775     else
13776       Bang(good_x, good_y);
13777   }
13778 }
13779
13780 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13781 {
13782   int i, kill_x = -1, kill_y = -1;
13783   int bad_element = Tile[bad_x][bad_y];
13784   static int test_xy[4][2] =
13785   {
13786     { 0, -1 },
13787     { -1, 0 },
13788     { +1, 0 },
13789     { 0, +1 }
13790   };
13791   static int touch_dir[4] =
13792   {
13793     MV_LEFT | MV_RIGHT,
13794     MV_UP   | MV_DOWN,
13795     MV_UP   | MV_DOWN,
13796     MV_LEFT | MV_RIGHT
13797   };
13798   static int test_dir[4] =
13799   {
13800     MV_UP,
13801     MV_LEFT,
13802     MV_RIGHT,
13803     MV_DOWN
13804   };
13805
13806   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13807     return;
13808
13809   for (i = 0; i < NUM_DIRECTIONS; i++)
13810   {
13811     int test_x, test_y, test_move_dir, test_element;
13812
13813     test_x = bad_x + test_xy[i][0];
13814     test_y = bad_y + test_xy[i][1];
13815
13816     if (!IN_LEV_FIELD(test_x, test_y))
13817       continue;
13818
13819     test_move_dir =
13820       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13821
13822     test_element = Tile[test_x][test_y];
13823
13824     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13825        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13826     */
13827     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13828         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13829     {
13830       // good thing is player or penguin that does not move away
13831       if (IS_PLAYER(test_x, test_y))
13832       {
13833         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13834
13835         if (bad_element == EL_ROBOT && player->is_moving)
13836           continue;     // robot does not kill player if he is moving
13837
13838         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13839         {
13840           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13841             continue;           // center and border element do not touch
13842         }
13843
13844         kill_x = test_x;
13845         kill_y = test_y;
13846
13847         break;
13848       }
13849       else if (test_element == EL_PENGUIN)
13850       {
13851         kill_x = test_x;
13852         kill_y = test_y;
13853
13854         break;
13855       }
13856     }
13857   }
13858
13859   if (kill_x != -1 || kill_y != -1)
13860   {
13861     if (IS_PLAYER(kill_x, kill_y))
13862     {
13863       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13864
13865       if (player->shield_deadly_time_left > 0 &&
13866           !IS_INDESTRUCTIBLE(bad_element))
13867         Bang(bad_x, bad_y);
13868       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13869         KillPlayer(player);
13870     }
13871     else
13872       Bang(kill_x, kill_y);
13873   }
13874 }
13875
13876 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13877 {
13878   int bad_element = Tile[bad_x][bad_y];
13879   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13880   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13881   int test_x = bad_x + dx, test_y = bad_y + dy;
13882   int test_move_dir, test_element;
13883   int kill_x = -1, kill_y = -1;
13884
13885   if (!IN_LEV_FIELD(test_x, test_y))
13886     return;
13887
13888   test_move_dir =
13889     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13890
13891   test_element = Tile[test_x][test_y];
13892
13893   if (test_move_dir != bad_move_dir)
13894   {
13895     // good thing can be player or penguin that does not move away
13896     if (IS_PLAYER(test_x, test_y))
13897     {
13898       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13899
13900       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13901          player as being hit when he is moving towards the bad thing, because
13902          the "get hit by" condition would be lost after the player stops) */
13903       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13904         return;         // player moves away from bad thing
13905
13906       kill_x = test_x;
13907       kill_y = test_y;
13908     }
13909     else if (test_element == EL_PENGUIN)
13910     {
13911       kill_x = test_x;
13912       kill_y = test_y;
13913     }
13914   }
13915
13916   if (kill_x != -1 || kill_y != -1)
13917   {
13918     if (IS_PLAYER(kill_x, kill_y))
13919     {
13920       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13921
13922       if (player->shield_deadly_time_left > 0 &&
13923           !IS_INDESTRUCTIBLE(bad_element))
13924         Bang(bad_x, bad_y);
13925       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13926         KillPlayer(player);
13927     }
13928     else
13929       Bang(kill_x, kill_y);
13930   }
13931 }
13932
13933 void TestIfPlayerTouchesBadThing(int x, int y)
13934 {
13935   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13936 }
13937
13938 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13939 {
13940   TestIfGoodThingHitsBadThing(x, y, move_dir);
13941 }
13942
13943 void TestIfBadThingTouchesPlayer(int x, int y)
13944 {
13945   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13946 }
13947
13948 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13949 {
13950   TestIfBadThingHitsGoodThing(x, y, move_dir);
13951 }
13952
13953 void TestIfFriendTouchesBadThing(int x, int y)
13954 {
13955   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13956 }
13957
13958 void TestIfBadThingTouchesFriend(int x, int y)
13959 {
13960   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13961 }
13962
13963 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13964 {
13965   int i, kill_x = bad_x, kill_y = bad_y;
13966   static int xy[4][2] =
13967   {
13968     { 0, -1 },
13969     { -1, 0 },
13970     { +1, 0 },
13971     { 0, +1 }
13972   };
13973
13974   for (i = 0; i < NUM_DIRECTIONS; i++)
13975   {
13976     int x, y, element;
13977
13978     x = bad_x + xy[i][0];
13979     y = bad_y + xy[i][1];
13980     if (!IN_LEV_FIELD(x, y))
13981       continue;
13982
13983     element = Tile[x][y];
13984     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13985         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13986     {
13987       kill_x = x;
13988       kill_y = y;
13989       break;
13990     }
13991   }
13992
13993   if (kill_x != bad_x || kill_y != bad_y)
13994     Bang(bad_x, bad_y);
13995 }
13996
13997 void KillPlayer(struct PlayerInfo *player)
13998 {
13999   int jx = player->jx, jy = player->jy;
14000
14001   if (!player->active)
14002     return;
14003
14004 #if 0
14005   Debug("game:playing:KillPlayer",
14006         "0: killed == %d, active == %d, reanimated == %d",
14007         player->killed, player->active, player->reanimated);
14008 #endif
14009
14010   /* the following code was introduced to prevent an infinite loop when calling
14011      -> Bang()
14012      -> CheckTriggeredElementChangeExt()
14013      -> ExecuteCustomElementAction()
14014      -> KillPlayer()
14015      -> (infinitely repeating the above sequence of function calls)
14016      which occurs when killing the player while having a CE with the setting
14017      "kill player X when explosion of <player X>"; the solution using a new
14018      field "player->killed" was chosen for backwards compatibility, although
14019      clever use of the fields "player->active" etc. would probably also work */
14020 #if 1
14021   if (player->killed)
14022     return;
14023 #endif
14024
14025   player->killed = TRUE;
14026
14027   // remove accessible field at the player's position
14028   Tile[jx][jy] = EL_EMPTY;
14029
14030   // deactivate shield (else Bang()/Explode() would not work right)
14031   player->shield_normal_time_left = 0;
14032   player->shield_deadly_time_left = 0;
14033
14034 #if 0
14035   Debug("game:playing:KillPlayer",
14036         "1: killed == %d, active == %d, reanimated == %d",
14037         player->killed, player->active, player->reanimated);
14038 #endif
14039
14040   Bang(jx, jy);
14041
14042 #if 0
14043   Debug("game:playing:KillPlayer",
14044         "2: killed == %d, active == %d, reanimated == %d",
14045         player->killed, player->active, player->reanimated);
14046 #endif
14047
14048   if (player->reanimated)       // killed player may have been reanimated
14049     player->killed = player->reanimated = FALSE;
14050   else
14051     BuryPlayer(player);
14052 }
14053
14054 static void KillPlayerUnlessEnemyProtected(int x, int y)
14055 {
14056   if (!PLAYER_ENEMY_PROTECTED(x, y))
14057     KillPlayer(PLAYERINFO(x, y));
14058 }
14059
14060 static void KillPlayerUnlessExplosionProtected(int x, int y)
14061 {
14062   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14063     KillPlayer(PLAYERINFO(x, y));
14064 }
14065
14066 void BuryPlayer(struct PlayerInfo *player)
14067 {
14068   int jx = player->jx, jy = player->jy;
14069
14070   if (!player->active)
14071     return;
14072
14073   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14074   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14075
14076   RemovePlayer(player);
14077
14078   player->buried = TRUE;
14079
14080   if (game.all_players_gone)
14081     game.GameOver = TRUE;
14082 }
14083
14084 void RemovePlayer(struct PlayerInfo *player)
14085 {
14086   int jx = player->jx, jy = player->jy;
14087   int i, found = FALSE;
14088
14089   player->present = FALSE;
14090   player->active = FALSE;
14091
14092   // required for some CE actions (even if the player is not active anymore)
14093   player->MovPos = 0;
14094
14095   if (!ExplodeField[jx][jy])
14096     StorePlayer[jx][jy] = 0;
14097
14098   if (player->is_moving)
14099     TEST_DrawLevelField(player->last_jx, player->last_jy);
14100
14101   for (i = 0; i < MAX_PLAYERS; i++)
14102     if (stored_player[i].active)
14103       found = TRUE;
14104
14105   if (!found)
14106   {
14107     game.all_players_gone = TRUE;
14108     game.GameOver = TRUE;
14109   }
14110
14111   game.exit_x = game.robot_wheel_x = jx;
14112   game.exit_y = game.robot_wheel_y = jy;
14113 }
14114
14115 void ExitPlayer(struct PlayerInfo *player)
14116 {
14117   DrawPlayer(player);   // needed here only to cleanup last field
14118   RemovePlayer(player);
14119
14120   if (game.players_still_needed > 0)
14121     game.players_still_needed--;
14122 }
14123
14124 static void SetFieldForSnapping(int x, int y, int element, int direction,
14125                                 int player_index_bit)
14126 {
14127   struct ElementInfo *ei = &element_info[element];
14128   int direction_bit = MV_DIR_TO_BIT(direction);
14129   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14130   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14131                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14132
14133   Tile[x][y] = EL_ELEMENT_SNAPPING;
14134   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14135   MovDir[x][y] = direction;
14136   Store[x][y] = element;
14137   Store2[x][y] = player_index_bit;
14138
14139   ResetGfxAnimation(x, y);
14140
14141   GfxElement[x][y] = element;
14142   GfxAction[x][y] = action;
14143   GfxDir[x][y] = direction;
14144   GfxFrame[x][y] = -1;
14145 }
14146
14147 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14148                                    int player_index_bit)
14149 {
14150   TestIfElementTouchesCustomElement(x, y);      // for empty space
14151
14152   if (level.finish_dig_collect)
14153   {
14154     int dig_side = MV_DIR_OPPOSITE(direction);
14155     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14156                         CE_PLAYER_COLLECTS_X);
14157
14158     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14159                                         player_index_bit, dig_side);
14160     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14161                                         player_index_bit, dig_side);
14162   }
14163 }
14164
14165 /*
14166   =============================================================================
14167   checkDiagonalPushing()
14168   -----------------------------------------------------------------------------
14169   check if diagonal input device direction results in pushing of object
14170   (by checking if the alternative direction is walkable, diggable, ...)
14171   =============================================================================
14172 */
14173
14174 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14175                                     int x, int y, int real_dx, int real_dy)
14176 {
14177   int jx, jy, dx, dy, xx, yy;
14178
14179   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14180     return TRUE;
14181
14182   // diagonal direction: check alternative direction
14183   jx = player->jx;
14184   jy = player->jy;
14185   dx = x - jx;
14186   dy = y - jy;
14187   xx = jx + (dx == 0 ? real_dx : 0);
14188   yy = jy + (dy == 0 ? real_dy : 0);
14189
14190   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14191 }
14192
14193 /*
14194   =============================================================================
14195   DigField()
14196   -----------------------------------------------------------------------------
14197   x, y:                 field next to player (non-diagonal) to try to dig to
14198   real_dx, real_dy:     direction as read from input device (can be diagonal)
14199   =============================================================================
14200 */
14201
14202 static int DigField(struct PlayerInfo *player,
14203                     int oldx, int oldy, int x, int y,
14204                     int real_dx, int real_dy, int mode)
14205 {
14206   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14207   boolean player_was_pushing = player->is_pushing;
14208   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14209   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14210   int jx = oldx, jy = oldy;
14211   int dx = x - jx, dy = y - jy;
14212   int nextx = x + dx, nexty = y + dy;
14213   int move_direction = (dx == -1 ? MV_LEFT  :
14214                         dx == +1 ? MV_RIGHT :
14215                         dy == -1 ? MV_UP    :
14216                         dy == +1 ? MV_DOWN  : MV_NONE);
14217   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14218   int dig_side = MV_DIR_OPPOSITE(move_direction);
14219   int old_element = Tile[jx][jy];
14220   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14221   int collect_count;
14222
14223   if (is_player)                // function can also be called by EL_PENGUIN
14224   {
14225     if (player->MovPos == 0)
14226     {
14227       player->is_digging = FALSE;
14228       player->is_collecting = FALSE;
14229     }
14230
14231     if (player->MovPos == 0)    // last pushing move finished
14232       player->is_pushing = FALSE;
14233
14234     if (mode == DF_NO_PUSH)     // player just stopped pushing
14235     {
14236       player->is_switching = FALSE;
14237       player->push_delay = -1;
14238
14239       return MP_NO_ACTION;
14240     }
14241   }
14242   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14243     old_element = Back[jx][jy];
14244
14245   // in case of element dropped at player position, check background
14246   else if (Back[jx][jy] != EL_EMPTY &&
14247            game.engine_version >= VERSION_IDENT(2,2,0,0))
14248     old_element = Back[jx][jy];
14249
14250   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14251     return MP_NO_ACTION;        // field has no opening in this direction
14252
14253   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14254     return MP_NO_ACTION;        // field has no opening in this direction
14255
14256   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14257   {
14258     SplashAcid(x, y);
14259
14260     Tile[jx][jy] = player->artwork_element;
14261     InitMovingField(jx, jy, MV_DOWN);
14262     Store[jx][jy] = EL_ACID;
14263     ContinueMoving(jx, jy);
14264     BuryPlayer(player);
14265
14266     return MP_DONT_RUN_INTO;
14267   }
14268
14269   if (player_can_move && DONT_RUN_INTO(element))
14270   {
14271     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14272
14273     return MP_DONT_RUN_INTO;
14274   }
14275
14276   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14277     return MP_NO_ACTION;
14278
14279   collect_count = element_info[element].collect_count_initial;
14280
14281   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14282     return MP_NO_ACTION;
14283
14284   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14285     player_can_move = player_can_move_or_snap;
14286
14287   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14288       game.engine_version >= VERSION_IDENT(2,2,0,0))
14289   {
14290     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14291                                player->index_bit, dig_side);
14292     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14293                                         player->index_bit, dig_side);
14294
14295     if (element == EL_DC_LANDMINE)
14296       Bang(x, y);
14297
14298     if (Tile[x][y] != element)          // field changed by snapping
14299       return MP_ACTION;
14300
14301     return MP_NO_ACTION;
14302   }
14303
14304   if (player->gravity && is_player && !player->is_auto_moving &&
14305       canFallDown(player) && move_direction != MV_DOWN &&
14306       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14307     return MP_NO_ACTION;        // player cannot walk here due to gravity
14308
14309   if (player_can_move &&
14310       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14311   {
14312     int sound_element = SND_ELEMENT(element);
14313     int sound_action = ACTION_WALKING;
14314
14315     if (IS_RND_GATE(element))
14316     {
14317       if (!player->key[RND_GATE_NR(element)])
14318         return MP_NO_ACTION;
14319     }
14320     else if (IS_RND_GATE_GRAY(element))
14321     {
14322       if (!player->key[RND_GATE_GRAY_NR(element)])
14323         return MP_NO_ACTION;
14324     }
14325     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14326     {
14327       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14328         return MP_NO_ACTION;
14329     }
14330     else if (element == EL_EXIT_OPEN ||
14331              element == EL_EM_EXIT_OPEN ||
14332              element == EL_EM_EXIT_OPENING ||
14333              element == EL_STEEL_EXIT_OPEN ||
14334              element == EL_EM_STEEL_EXIT_OPEN ||
14335              element == EL_EM_STEEL_EXIT_OPENING ||
14336              element == EL_SP_EXIT_OPEN ||
14337              element == EL_SP_EXIT_OPENING)
14338     {
14339       sound_action = ACTION_PASSING;    // player is passing exit
14340     }
14341     else if (element == EL_EMPTY)
14342     {
14343       sound_action = ACTION_MOVING;             // nothing to walk on
14344     }
14345
14346     // play sound from background or player, whatever is available
14347     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14348       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14349     else
14350       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14351   }
14352   else if (player_can_move &&
14353            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14354   {
14355     if (!ACCESS_FROM(element, opposite_direction))
14356       return MP_NO_ACTION;      // field not accessible from this direction
14357
14358     if (CAN_MOVE(element))      // only fixed elements can be passed!
14359       return MP_NO_ACTION;
14360
14361     if (IS_EM_GATE(element))
14362     {
14363       if (!player->key[EM_GATE_NR(element)])
14364         return MP_NO_ACTION;
14365     }
14366     else if (IS_EM_GATE_GRAY(element))
14367     {
14368       if (!player->key[EM_GATE_GRAY_NR(element)])
14369         return MP_NO_ACTION;
14370     }
14371     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14372     {
14373       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14374         return MP_NO_ACTION;
14375     }
14376     else if (IS_EMC_GATE(element))
14377     {
14378       if (!player->key[EMC_GATE_NR(element)])
14379         return MP_NO_ACTION;
14380     }
14381     else if (IS_EMC_GATE_GRAY(element))
14382     {
14383       if (!player->key[EMC_GATE_GRAY_NR(element)])
14384         return MP_NO_ACTION;
14385     }
14386     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14387     {
14388       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14389         return MP_NO_ACTION;
14390     }
14391     else if (element == EL_DC_GATE_WHITE ||
14392              element == EL_DC_GATE_WHITE_GRAY ||
14393              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14394     {
14395       if (player->num_white_keys == 0)
14396         return MP_NO_ACTION;
14397
14398       player->num_white_keys--;
14399     }
14400     else if (IS_SP_PORT(element))
14401     {
14402       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14403           element == EL_SP_GRAVITY_PORT_RIGHT ||
14404           element == EL_SP_GRAVITY_PORT_UP ||
14405           element == EL_SP_GRAVITY_PORT_DOWN)
14406         player->gravity = !player->gravity;
14407       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14408                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14409                element == EL_SP_GRAVITY_ON_PORT_UP ||
14410                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14411         player->gravity = TRUE;
14412       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14413                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14414                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14415                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14416         player->gravity = FALSE;
14417     }
14418
14419     // automatically move to the next field with double speed
14420     player->programmed_action = move_direction;
14421
14422     if (player->move_delay_reset_counter == 0)
14423     {
14424       player->move_delay_reset_counter = 2;     // two double speed steps
14425
14426       DOUBLE_PLAYER_SPEED(player);
14427     }
14428
14429     PlayLevelSoundAction(x, y, ACTION_PASSING);
14430   }
14431   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14432   {
14433     RemoveField(x, y);
14434
14435     if (mode != DF_SNAP)
14436     {
14437       GfxElement[x][y] = GFX_ELEMENT(element);
14438       player->is_digging = TRUE;
14439     }
14440
14441     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14442
14443     // use old behaviour for old levels (digging)
14444     if (!level.finish_dig_collect)
14445     {
14446       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14447                                           player->index_bit, dig_side);
14448
14449       // if digging triggered player relocation, finish digging tile
14450       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14451         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14452     }
14453
14454     if (mode == DF_SNAP)
14455     {
14456       if (level.block_snap_field)
14457         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14458       else
14459         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14460
14461       // use old behaviour for old levels (snapping)
14462       if (!level.finish_dig_collect)
14463         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14464                                             player->index_bit, dig_side);
14465     }
14466   }
14467   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14468   {
14469     RemoveField(x, y);
14470
14471     if (is_player && mode != DF_SNAP)
14472     {
14473       GfxElement[x][y] = element;
14474       player->is_collecting = TRUE;
14475     }
14476
14477     if (element == EL_SPEED_PILL)
14478     {
14479       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14480     }
14481     else if (element == EL_EXTRA_TIME && level.time > 0)
14482     {
14483       TimeLeft += level.extra_time;
14484
14485       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14486
14487       DisplayGameControlValues();
14488     }
14489     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14490     {
14491       player->shield_normal_time_left += level.shield_normal_time;
14492       if (element == EL_SHIELD_DEADLY)
14493         player->shield_deadly_time_left += level.shield_deadly_time;
14494     }
14495     else if (element == EL_DYNAMITE ||
14496              element == EL_EM_DYNAMITE ||
14497              element == EL_SP_DISK_RED)
14498     {
14499       if (player->inventory_size < MAX_INVENTORY_SIZE)
14500         player->inventory_element[player->inventory_size++] = element;
14501
14502       DrawGameDoorValues();
14503     }
14504     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14505     {
14506       player->dynabomb_count++;
14507       player->dynabombs_left++;
14508     }
14509     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14510     {
14511       player->dynabomb_size++;
14512     }
14513     else if (element == EL_DYNABOMB_INCREASE_POWER)
14514     {
14515       player->dynabomb_xl = TRUE;
14516     }
14517     else if (IS_KEY(element))
14518     {
14519       player->key[KEY_NR(element)] = TRUE;
14520
14521       DrawGameDoorValues();
14522     }
14523     else if (element == EL_DC_KEY_WHITE)
14524     {
14525       player->num_white_keys++;
14526
14527       // display white keys?
14528       // DrawGameDoorValues();
14529     }
14530     else if (IS_ENVELOPE(element))
14531     {
14532       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14533
14534       if (!wait_for_snapping)
14535         player->show_envelope = element;
14536     }
14537     else if (element == EL_EMC_LENSES)
14538     {
14539       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14540
14541       RedrawAllInvisibleElementsForLenses();
14542     }
14543     else if (element == EL_EMC_MAGNIFIER)
14544     {
14545       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14546
14547       RedrawAllInvisibleElementsForMagnifier();
14548     }
14549     else if (IS_DROPPABLE(element) ||
14550              IS_THROWABLE(element))     // can be collected and dropped
14551     {
14552       int i;
14553
14554       if (collect_count == 0)
14555         player->inventory_infinite_element = element;
14556       else
14557         for (i = 0; i < collect_count; i++)
14558           if (player->inventory_size < MAX_INVENTORY_SIZE)
14559             player->inventory_element[player->inventory_size++] = element;
14560
14561       DrawGameDoorValues();
14562     }
14563     else if (collect_count > 0)
14564     {
14565       game.gems_still_needed -= collect_count;
14566       if (game.gems_still_needed < 0)
14567         game.gems_still_needed = 0;
14568
14569       game.snapshot.collected_item = TRUE;
14570
14571       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14572
14573       DisplayGameControlValues();
14574     }
14575
14576     RaiseScoreElement(element);
14577     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14578
14579     // use old behaviour for old levels (collecting)
14580     if (!level.finish_dig_collect && is_player)
14581     {
14582       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14583                                           player->index_bit, dig_side);
14584
14585       // if collecting triggered player relocation, finish collecting tile
14586       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14587         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14588     }
14589
14590     if (mode == DF_SNAP)
14591     {
14592       if (level.block_snap_field)
14593         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14594       else
14595         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14596
14597       // use old behaviour for old levels (snapping)
14598       if (!level.finish_dig_collect)
14599         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14600                                             player->index_bit, dig_side);
14601     }
14602   }
14603   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14604   {
14605     if (mode == DF_SNAP && element != EL_BD_ROCK)
14606       return MP_NO_ACTION;
14607
14608     if (CAN_FALL(element) && dy)
14609       return MP_NO_ACTION;
14610
14611     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14612         !(element == EL_SPRING && level.use_spring_bug))
14613       return MP_NO_ACTION;
14614
14615     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14616         ((move_direction & MV_VERTICAL &&
14617           ((element_info[element].move_pattern & MV_LEFT &&
14618             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14619            (element_info[element].move_pattern & MV_RIGHT &&
14620             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14621          (move_direction & MV_HORIZONTAL &&
14622           ((element_info[element].move_pattern & MV_UP &&
14623             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14624            (element_info[element].move_pattern & MV_DOWN &&
14625             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14626       return MP_NO_ACTION;
14627
14628     // do not push elements already moving away faster than player
14629     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14630         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14631       return MP_NO_ACTION;
14632
14633     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14634     {
14635       if (player->push_delay_value == -1 || !player_was_pushing)
14636         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14637     }
14638     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14639     {
14640       if (player->push_delay_value == -1)
14641         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14642     }
14643     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14644     {
14645       if (!player->is_pushing)
14646         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14647     }
14648
14649     player->is_pushing = TRUE;
14650     player->is_active = TRUE;
14651
14652     if (!(IN_LEV_FIELD(nextx, nexty) &&
14653           (IS_FREE(nextx, nexty) ||
14654            (IS_SB_ELEMENT(element) &&
14655             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14656            (IS_CUSTOM_ELEMENT(element) &&
14657             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14658       return MP_NO_ACTION;
14659
14660     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14661       return MP_NO_ACTION;
14662
14663     if (player->push_delay == -1)       // new pushing; restart delay
14664       player->push_delay = 0;
14665
14666     if (player->push_delay < player->push_delay_value &&
14667         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14668         element != EL_SPRING && element != EL_BALLOON)
14669     {
14670       // make sure that there is no move delay before next try to push
14671       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14672         player->move_delay = 0;
14673
14674       return MP_NO_ACTION;
14675     }
14676
14677     if (IS_CUSTOM_ELEMENT(element) &&
14678         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14679     {
14680       if (!DigFieldByCE(nextx, nexty, element))
14681         return MP_NO_ACTION;
14682     }
14683
14684     if (IS_SB_ELEMENT(element))
14685     {
14686       boolean sokoban_task_solved = FALSE;
14687
14688       if (element == EL_SOKOBAN_FIELD_FULL)
14689       {
14690         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14691
14692         IncrementSokobanFieldsNeeded();
14693         IncrementSokobanObjectsNeeded();
14694       }
14695
14696       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14697       {
14698         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14699
14700         DecrementSokobanFieldsNeeded();
14701         DecrementSokobanObjectsNeeded();
14702
14703         // sokoban object was pushed from empty field to sokoban field
14704         if (Back[x][y] == EL_EMPTY)
14705           sokoban_task_solved = TRUE;
14706       }
14707
14708       Tile[x][y] = EL_SOKOBAN_OBJECT;
14709
14710       if (Back[x][y] == Back[nextx][nexty])
14711         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14712       else if (Back[x][y] != 0)
14713         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14714                                     ACTION_EMPTYING);
14715       else
14716         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14717                                     ACTION_FILLING);
14718
14719       if (sokoban_task_solved &&
14720           game.sokoban_fields_still_needed == 0 &&
14721           game.sokoban_objects_still_needed == 0 &&
14722           level.auto_exit_sokoban)
14723       {
14724         game.players_still_needed = 0;
14725
14726         LevelSolved();
14727
14728         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14729       }
14730     }
14731     else
14732       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14733
14734     InitMovingField(x, y, move_direction);
14735     GfxAction[x][y] = ACTION_PUSHING;
14736
14737     if (mode == DF_SNAP)
14738       ContinueMoving(x, y);
14739     else
14740       MovPos[x][y] = (dx != 0 ? dx : dy);
14741
14742     Pushed[x][y] = TRUE;
14743     Pushed[nextx][nexty] = TRUE;
14744
14745     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14746       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14747     else
14748       player->push_delay_value = -1;    // get new value later
14749
14750     // check for element change _after_ element has been pushed
14751     if (game.use_change_when_pushing_bug)
14752     {
14753       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14754                                  player->index_bit, dig_side);
14755       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14756                                           player->index_bit, dig_side);
14757     }
14758   }
14759   else if (IS_SWITCHABLE(element))
14760   {
14761     if (PLAYER_SWITCHING(player, x, y))
14762     {
14763       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14764                                           player->index_bit, dig_side);
14765
14766       return MP_ACTION;
14767     }
14768
14769     player->is_switching = TRUE;
14770     player->switch_x = x;
14771     player->switch_y = y;
14772
14773     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14774
14775     if (element == EL_ROBOT_WHEEL)
14776     {
14777       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14778
14779       game.robot_wheel_x = x;
14780       game.robot_wheel_y = y;
14781       game.robot_wheel_active = TRUE;
14782
14783       TEST_DrawLevelField(x, y);
14784     }
14785     else if (element == EL_SP_TERMINAL)
14786     {
14787       int xx, yy;
14788
14789       SCAN_PLAYFIELD(xx, yy)
14790       {
14791         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14792         {
14793           Bang(xx, yy);
14794         }
14795         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14796         {
14797           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14798
14799           ResetGfxAnimation(xx, yy);
14800           TEST_DrawLevelField(xx, yy);
14801         }
14802       }
14803     }
14804     else if (IS_BELT_SWITCH(element))
14805     {
14806       ToggleBeltSwitch(x, y);
14807     }
14808     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14809              element == EL_SWITCHGATE_SWITCH_DOWN ||
14810              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14811              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14812     {
14813       ToggleSwitchgateSwitch(x, y);
14814     }
14815     else if (element == EL_LIGHT_SWITCH ||
14816              element == EL_LIGHT_SWITCH_ACTIVE)
14817     {
14818       ToggleLightSwitch(x, y);
14819     }
14820     else if (element == EL_TIMEGATE_SWITCH ||
14821              element == EL_DC_TIMEGATE_SWITCH)
14822     {
14823       ActivateTimegateSwitch(x, y);
14824     }
14825     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14826              element == EL_BALLOON_SWITCH_RIGHT ||
14827              element == EL_BALLOON_SWITCH_UP    ||
14828              element == EL_BALLOON_SWITCH_DOWN  ||
14829              element == EL_BALLOON_SWITCH_NONE  ||
14830              element == EL_BALLOON_SWITCH_ANY)
14831     {
14832       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14833                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14834                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14835                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14836                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14837                              move_direction);
14838     }
14839     else if (element == EL_LAMP)
14840     {
14841       Tile[x][y] = EL_LAMP_ACTIVE;
14842       game.lights_still_needed--;
14843
14844       ResetGfxAnimation(x, y);
14845       TEST_DrawLevelField(x, y);
14846     }
14847     else if (element == EL_TIME_ORB_FULL)
14848     {
14849       Tile[x][y] = EL_TIME_ORB_EMPTY;
14850
14851       if (level.time > 0 || level.use_time_orb_bug)
14852       {
14853         TimeLeft += level.time_orb_time;
14854         game.no_time_limit = FALSE;
14855
14856         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14857
14858         DisplayGameControlValues();
14859       }
14860
14861       ResetGfxAnimation(x, y);
14862       TEST_DrawLevelField(x, y);
14863     }
14864     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14865              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14866     {
14867       int xx, yy;
14868
14869       game.ball_active = !game.ball_active;
14870
14871       SCAN_PLAYFIELD(xx, yy)
14872       {
14873         int e = Tile[xx][yy];
14874
14875         if (game.ball_active)
14876         {
14877           if (e == EL_EMC_MAGIC_BALL)
14878             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14879           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14880             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14881         }
14882         else
14883         {
14884           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14885             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14886           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14887             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14888         }
14889       }
14890     }
14891
14892     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14893                                         player->index_bit, dig_side);
14894
14895     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14896                                         player->index_bit, dig_side);
14897
14898     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14899                                         player->index_bit, dig_side);
14900
14901     return MP_ACTION;
14902   }
14903   else
14904   {
14905     if (!PLAYER_SWITCHING(player, x, y))
14906     {
14907       player->is_switching = TRUE;
14908       player->switch_x = x;
14909       player->switch_y = y;
14910
14911       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14912                                  player->index_bit, dig_side);
14913       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14914                                           player->index_bit, dig_side);
14915
14916       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14917                                  player->index_bit, dig_side);
14918       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14919                                           player->index_bit, dig_side);
14920     }
14921
14922     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14923                                player->index_bit, dig_side);
14924     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14925                                         player->index_bit, dig_side);
14926
14927     return MP_NO_ACTION;
14928   }
14929
14930   player->push_delay = -1;
14931
14932   if (is_player)                // function can also be called by EL_PENGUIN
14933   {
14934     if (Tile[x][y] != element)          // really digged/collected something
14935     {
14936       player->is_collecting = !player->is_digging;
14937       player->is_active = TRUE;
14938
14939       player->last_removed_element = element;
14940     }
14941   }
14942
14943   return MP_MOVING;
14944 }
14945
14946 static boolean DigFieldByCE(int x, int y, int digging_element)
14947 {
14948   int element = Tile[x][y];
14949
14950   if (!IS_FREE(x, y))
14951   {
14952     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14953                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14954                   ACTION_BREAKING);
14955
14956     // no element can dig solid indestructible elements
14957     if (IS_INDESTRUCTIBLE(element) &&
14958         !IS_DIGGABLE(element) &&
14959         !IS_COLLECTIBLE(element))
14960       return FALSE;
14961
14962     if (AmoebaNr[x][y] &&
14963         (element == EL_AMOEBA_FULL ||
14964          element == EL_BD_AMOEBA ||
14965          element == EL_AMOEBA_GROWING))
14966     {
14967       AmoebaCnt[AmoebaNr[x][y]]--;
14968       AmoebaCnt2[AmoebaNr[x][y]]--;
14969     }
14970
14971     if (IS_MOVING(x, y))
14972       RemoveMovingField(x, y);
14973     else
14974     {
14975       RemoveField(x, y);
14976       TEST_DrawLevelField(x, y);
14977     }
14978
14979     // if digged element was about to explode, prevent the explosion
14980     ExplodeField[x][y] = EX_TYPE_NONE;
14981
14982     PlayLevelSoundAction(x, y, action);
14983   }
14984
14985   Store[x][y] = EL_EMPTY;
14986
14987   // this makes it possible to leave the removed element again
14988   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14989     Store[x][y] = element;
14990
14991   return TRUE;
14992 }
14993
14994 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14995 {
14996   int jx = player->jx, jy = player->jy;
14997   int x = jx + dx, y = jy + dy;
14998   int snap_direction = (dx == -1 ? MV_LEFT  :
14999                         dx == +1 ? MV_RIGHT :
15000                         dy == -1 ? MV_UP    :
15001                         dy == +1 ? MV_DOWN  : MV_NONE);
15002   boolean can_continue_snapping = (level.continuous_snapping &&
15003                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15004
15005   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15006     return FALSE;
15007
15008   if (!player->active || !IN_LEV_FIELD(x, y))
15009     return FALSE;
15010
15011   if (dx && dy)
15012     return FALSE;
15013
15014   if (!dx && !dy)
15015   {
15016     if (player->MovPos == 0)
15017       player->is_pushing = FALSE;
15018
15019     player->is_snapping = FALSE;
15020
15021     if (player->MovPos == 0)
15022     {
15023       player->is_moving = FALSE;
15024       player->is_digging = FALSE;
15025       player->is_collecting = FALSE;
15026     }
15027
15028     return FALSE;
15029   }
15030
15031   // prevent snapping with already pressed snap key when not allowed
15032   if (player->is_snapping && !can_continue_snapping)
15033     return FALSE;
15034
15035   player->MovDir = snap_direction;
15036
15037   if (player->MovPos == 0)
15038   {
15039     player->is_moving = FALSE;
15040     player->is_digging = FALSE;
15041     player->is_collecting = FALSE;
15042   }
15043
15044   player->is_dropping = FALSE;
15045   player->is_dropping_pressed = FALSE;
15046   player->drop_pressed_delay = 0;
15047
15048   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15049     return FALSE;
15050
15051   player->is_snapping = TRUE;
15052   player->is_active = TRUE;
15053
15054   if (player->MovPos == 0)
15055   {
15056     player->is_moving = FALSE;
15057     player->is_digging = FALSE;
15058     player->is_collecting = FALSE;
15059   }
15060
15061   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15062     TEST_DrawLevelField(player->last_jx, player->last_jy);
15063
15064   TEST_DrawLevelField(x, y);
15065
15066   return TRUE;
15067 }
15068
15069 static boolean DropElement(struct PlayerInfo *player)
15070 {
15071   int old_element, new_element;
15072   int dropx = player->jx, dropy = player->jy;
15073   int drop_direction = player->MovDir;
15074   int drop_side = drop_direction;
15075   int drop_element = get_next_dropped_element(player);
15076
15077   /* do not drop an element on top of another element; when holding drop key
15078      pressed without moving, dropped element must move away before the next
15079      element can be dropped (this is especially important if the next element
15080      is dynamite, which can be placed on background for historical reasons) */
15081   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15082     return MP_ACTION;
15083
15084   if (IS_THROWABLE(drop_element))
15085   {
15086     dropx += GET_DX_FROM_DIR(drop_direction);
15087     dropy += GET_DY_FROM_DIR(drop_direction);
15088
15089     if (!IN_LEV_FIELD(dropx, dropy))
15090       return FALSE;
15091   }
15092
15093   old_element = Tile[dropx][dropy];     // old element at dropping position
15094   new_element = drop_element;           // default: no change when dropping
15095
15096   // check if player is active, not moving and ready to drop
15097   if (!player->active || player->MovPos || player->drop_delay > 0)
15098     return FALSE;
15099
15100   // check if player has anything that can be dropped
15101   if (new_element == EL_UNDEFINED)
15102     return FALSE;
15103
15104   // only set if player has anything that can be dropped
15105   player->is_dropping_pressed = TRUE;
15106
15107   // check if drop key was pressed long enough for EM style dynamite
15108   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15109     return FALSE;
15110
15111   // check if anything can be dropped at the current position
15112   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15113     return FALSE;
15114
15115   // collected custom elements can only be dropped on empty fields
15116   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15117     return FALSE;
15118
15119   if (old_element != EL_EMPTY)
15120     Back[dropx][dropy] = old_element;   // store old element on this field
15121
15122   ResetGfxAnimation(dropx, dropy);
15123   ResetRandomAnimationValue(dropx, dropy);
15124
15125   if (player->inventory_size > 0 ||
15126       player->inventory_infinite_element != EL_UNDEFINED)
15127   {
15128     if (player->inventory_size > 0)
15129     {
15130       player->inventory_size--;
15131
15132       DrawGameDoorValues();
15133
15134       if (new_element == EL_DYNAMITE)
15135         new_element = EL_DYNAMITE_ACTIVE;
15136       else if (new_element == EL_EM_DYNAMITE)
15137         new_element = EL_EM_DYNAMITE_ACTIVE;
15138       else if (new_element == EL_SP_DISK_RED)
15139         new_element = EL_SP_DISK_RED_ACTIVE;
15140     }
15141
15142     Tile[dropx][dropy] = new_element;
15143
15144     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15145       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15146                           el2img(Tile[dropx][dropy]), 0);
15147
15148     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15149
15150     // needed if previous element just changed to "empty" in the last frame
15151     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15152
15153     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15154                                player->index_bit, drop_side);
15155     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15156                                         CE_PLAYER_DROPS_X,
15157                                         player->index_bit, drop_side);
15158
15159     TestIfElementTouchesCustomElement(dropx, dropy);
15160   }
15161   else          // player is dropping a dyna bomb
15162   {
15163     player->dynabombs_left--;
15164
15165     Tile[dropx][dropy] = new_element;
15166
15167     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15168       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15169                           el2img(Tile[dropx][dropy]), 0);
15170
15171     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15172   }
15173
15174   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15175     InitField_WithBug1(dropx, dropy, FALSE);
15176
15177   new_element = Tile[dropx][dropy];     // element might have changed
15178
15179   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15180       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15181   {
15182     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15183       MovDir[dropx][dropy] = drop_direction;
15184
15185     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15186
15187     // do not cause impact style collision by dropping elements that can fall
15188     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15189   }
15190
15191   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15192   player->is_dropping = TRUE;
15193
15194   player->drop_pressed_delay = 0;
15195   player->is_dropping_pressed = FALSE;
15196
15197   player->drop_x = dropx;
15198   player->drop_y = dropy;
15199
15200   return TRUE;
15201 }
15202
15203 // ----------------------------------------------------------------------------
15204 // game sound playing functions
15205 // ----------------------------------------------------------------------------
15206
15207 static int *loop_sound_frame = NULL;
15208 static int *loop_sound_volume = NULL;
15209
15210 void InitPlayLevelSound(void)
15211 {
15212   int num_sounds = getSoundListSize();
15213
15214   checked_free(loop_sound_frame);
15215   checked_free(loop_sound_volume);
15216
15217   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15218   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15219 }
15220
15221 static void PlayLevelSound(int x, int y, int nr)
15222 {
15223   int sx = SCREENX(x), sy = SCREENY(y);
15224   int volume, stereo_position;
15225   int max_distance = 8;
15226   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15227
15228   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15229       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15230     return;
15231
15232   if (!IN_LEV_FIELD(x, y) ||
15233       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15234       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15235     return;
15236
15237   volume = SOUND_MAX_VOLUME;
15238
15239   if (!IN_SCR_FIELD(sx, sy))
15240   {
15241     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15242     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15243
15244     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15245   }
15246
15247   stereo_position = (SOUND_MAX_LEFT +
15248                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15249                      (SCR_FIELDX + 2 * max_distance));
15250
15251   if (IS_LOOP_SOUND(nr))
15252   {
15253     /* This assures that quieter loop sounds do not overwrite louder ones,
15254        while restarting sound volume comparison with each new game frame. */
15255
15256     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15257       return;
15258
15259     loop_sound_volume[nr] = volume;
15260     loop_sound_frame[nr] = FrameCounter;
15261   }
15262
15263   PlaySoundExt(nr, volume, stereo_position, type);
15264 }
15265
15266 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15267 {
15268   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15269                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15270                  y < LEVELY(BY1) ? LEVELY(BY1) :
15271                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15272                  sound_action);
15273 }
15274
15275 static void PlayLevelSoundAction(int x, int y, int action)
15276 {
15277   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15278 }
15279
15280 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15281 {
15282   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15283
15284   if (sound_effect != SND_UNDEFINED)
15285     PlayLevelSound(x, y, sound_effect);
15286 }
15287
15288 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15289                                               int action)
15290 {
15291   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15292
15293   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15294     PlayLevelSound(x, y, sound_effect);
15295 }
15296
15297 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15298 {
15299   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15300
15301   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15302     PlayLevelSound(x, y, sound_effect);
15303 }
15304
15305 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15306 {
15307   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15308
15309   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15310     StopSound(sound_effect);
15311 }
15312
15313 static int getLevelMusicNr(void)
15314 {
15315   if (levelset.music[level_nr] != MUS_UNDEFINED)
15316     return levelset.music[level_nr];            // from config file
15317   else
15318     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15319 }
15320
15321 static void FadeLevelSounds(void)
15322 {
15323   FadeSounds();
15324 }
15325
15326 static void FadeLevelMusic(void)
15327 {
15328   int music_nr = getLevelMusicNr();
15329   char *curr_music = getCurrentlyPlayingMusicFilename();
15330   char *next_music = getMusicInfoEntryFilename(music_nr);
15331
15332   if (!strEqual(curr_music, next_music))
15333     FadeMusic();
15334 }
15335
15336 void FadeLevelSoundsAndMusic(void)
15337 {
15338   FadeLevelSounds();
15339   FadeLevelMusic();
15340 }
15341
15342 static void PlayLevelMusic(void)
15343 {
15344   int music_nr = getLevelMusicNr();
15345   char *curr_music = getCurrentlyPlayingMusicFilename();
15346   char *next_music = getMusicInfoEntryFilename(music_nr);
15347
15348   if (!strEqual(curr_music, next_music))
15349     PlayMusicLoop(music_nr);
15350 }
15351
15352 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15353 {
15354   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15355   int offset = 0;
15356   int x = xx - offset;
15357   int y = yy - offset;
15358
15359   switch (sample)
15360   {
15361     case SOUND_blank:
15362       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15363       break;
15364
15365     case SOUND_roll:
15366       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15367       break;
15368
15369     case SOUND_stone:
15370       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15371       break;
15372
15373     case SOUND_nut:
15374       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15375       break;
15376
15377     case SOUND_crack:
15378       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15379       break;
15380
15381     case SOUND_bug:
15382       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15383       break;
15384
15385     case SOUND_tank:
15386       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15387       break;
15388
15389     case SOUND_android_clone:
15390       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15391       break;
15392
15393     case SOUND_android_move:
15394       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15395       break;
15396
15397     case SOUND_spring:
15398       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15399       break;
15400
15401     case SOUND_slurp:
15402       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15403       break;
15404
15405     case SOUND_eater:
15406       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15407       break;
15408
15409     case SOUND_eater_eat:
15410       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15411       break;
15412
15413     case SOUND_alien:
15414       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15415       break;
15416
15417     case SOUND_collect:
15418       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15419       break;
15420
15421     case SOUND_diamond:
15422       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15423       break;
15424
15425     case SOUND_squash:
15426       // !!! CHECK THIS !!!
15427 #if 1
15428       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15429 #else
15430       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15431 #endif
15432       break;
15433
15434     case SOUND_wonderfall:
15435       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15436       break;
15437
15438     case SOUND_drip:
15439       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15440       break;
15441
15442     case SOUND_push:
15443       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15444       break;
15445
15446     case SOUND_dirt:
15447       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15448       break;
15449
15450     case SOUND_acid:
15451       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15452       break;
15453
15454     case SOUND_ball:
15455       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15456       break;
15457
15458     case SOUND_slide:
15459       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15460       break;
15461
15462     case SOUND_wonder:
15463       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15464       break;
15465
15466     case SOUND_door:
15467       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15468       break;
15469
15470     case SOUND_exit_open:
15471       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15472       break;
15473
15474     case SOUND_exit_leave:
15475       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15476       break;
15477
15478     case SOUND_dynamite:
15479       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15480       break;
15481
15482     case SOUND_tick:
15483       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15484       break;
15485
15486     case SOUND_press:
15487       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15488       break;
15489
15490     case SOUND_wheel:
15491       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15492       break;
15493
15494     case SOUND_boom:
15495       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15496       break;
15497
15498     case SOUND_die:
15499       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15500       break;
15501
15502     case SOUND_time:
15503       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15504       break;
15505
15506     default:
15507       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15508       break;
15509   }
15510 }
15511
15512 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15513 {
15514   int element = map_element_SP_to_RND(element_sp);
15515   int action = map_action_SP_to_RND(action_sp);
15516   int offset = (setup.sp_show_border_elements ? 0 : 1);
15517   int x = xx - offset;
15518   int y = yy - offset;
15519
15520   PlayLevelSoundElementAction(x, y, element, action);
15521 }
15522
15523 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15524 {
15525   int element = map_element_MM_to_RND(element_mm);
15526   int action = map_action_MM_to_RND(action_mm);
15527   int offset = 0;
15528   int x = xx - offset;
15529   int y = yy - offset;
15530
15531   if (!IS_MM_ELEMENT(element))
15532     element = EL_MM_DEFAULT;
15533
15534   PlayLevelSoundElementAction(x, y, element, action);
15535 }
15536
15537 void PlaySound_MM(int sound_mm)
15538 {
15539   int sound = map_sound_MM_to_RND(sound_mm);
15540
15541   if (sound == SND_UNDEFINED)
15542     return;
15543
15544   PlaySound(sound);
15545 }
15546
15547 void PlaySoundLoop_MM(int sound_mm)
15548 {
15549   int sound = map_sound_MM_to_RND(sound_mm);
15550
15551   if (sound == SND_UNDEFINED)
15552     return;
15553
15554   PlaySoundLoop(sound);
15555 }
15556
15557 void StopSound_MM(int sound_mm)
15558 {
15559   int sound = map_sound_MM_to_RND(sound_mm);
15560
15561   if (sound == SND_UNDEFINED)
15562     return;
15563
15564   StopSound(sound);
15565 }
15566
15567 void RaiseScore(int value)
15568 {
15569   game.score += value;
15570
15571   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15572
15573   DisplayGameControlValues();
15574 }
15575
15576 void RaiseScoreElement(int element)
15577 {
15578   switch (element)
15579   {
15580     case EL_EMERALD:
15581     case EL_BD_DIAMOND:
15582     case EL_EMERALD_YELLOW:
15583     case EL_EMERALD_RED:
15584     case EL_EMERALD_PURPLE:
15585     case EL_SP_INFOTRON:
15586       RaiseScore(level.score[SC_EMERALD]);
15587       break;
15588     case EL_DIAMOND:
15589       RaiseScore(level.score[SC_DIAMOND]);
15590       break;
15591     case EL_CRYSTAL:
15592       RaiseScore(level.score[SC_CRYSTAL]);
15593       break;
15594     case EL_PEARL:
15595       RaiseScore(level.score[SC_PEARL]);
15596       break;
15597     case EL_BUG:
15598     case EL_BD_BUTTERFLY:
15599     case EL_SP_ELECTRON:
15600       RaiseScore(level.score[SC_BUG]);
15601       break;
15602     case EL_SPACESHIP:
15603     case EL_BD_FIREFLY:
15604     case EL_SP_SNIKSNAK:
15605       RaiseScore(level.score[SC_SPACESHIP]);
15606       break;
15607     case EL_YAMYAM:
15608     case EL_DARK_YAMYAM:
15609       RaiseScore(level.score[SC_YAMYAM]);
15610       break;
15611     case EL_ROBOT:
15612       RaiseScore(level.score[SC_ROBOT]);
15613       break;
15614     case EL_PACMAN:
15615       RaiseScore(level.score[SC_PACMAN]);
15616       break;
15617     case EL_NUT:
15618       RaiseScore(level.score[SC_NUT]);
15619       break;
15620     case EL_DYNAMITE:
15621     case EL_EM_DYNAMITE:
15622     case EL_SP_DISK_RED:
15623     case EL_DYNABOMB_INCREASE_NUMBER:
15624     case EL_DYNABOMB_INCREASE_SIZE:
15625     case EL_DYNABOMB_INCREASE_POWER:
15626       RaiseScore(level.score[SC_DYNAMITE]);
15627       break;
15628     case EL_SHIELD_NORMAL:
15629     case EL_SHIELD_DEADLY:
15630       RaiseScore(level.score[SC_SHIELD]);
15631       break;
15632     case EL_EXTRA_TIME:
15633       RaiseScore(level.extra_time_score);
15634       break;
15635     case EL_KEY_1:
15636     case EL_KEY_2:
15637     case EL_KEY_3:
15638     case EL_KEY_4:
15639     case EL_EM_KEY_1:
15640     case EL_EM_KEY_2:
15641     case EL_EM_KEY_3:
15642     case EL_EM_KEY_4:
15643     case EL_EMC_KEY_5:
15644     case EL_EMC_KEY_6:
15645     case EL_EMC_KEY_7:
15646     case EL_EMC_KEY_8:
15647     case EL_DC_KEY_WHITE:
15648       RaiseScore(level.score[SC_KEY]);
15649       break;
15650     default:
15651       RaiseScore(element_info[element].collect_score);
15652       break;
15653   }
15654 }
15655
15656 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15657 {
15658   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15659   {
15660     if (!quick_quit)
15661     {
15662       // prevent short reactivation of overlay buttons while closing door
15663       SetOverlayActive(FALSE);
15664
15665       // door may still be open due to skipped or envelope style request
15666       CloseDoor(DOOR_CLOSE_1);
15667     }
15668
15669     if (network.enabled)
15670       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15671     else
15672     {
15673       if (quick_quit)
15674         FadeSkipNextFadeIn();
15675
15676       SetGameStatus(GAME_MODE_MAIN);
15677
15678       DrawMainMenu();
15679     }
15680   }
15681   else          // continue playing the game
15682   {
15683     if (tape.playing && tape.deactivate_display)
15684       TapeDeactivateDisplayOff(TRUE);
15685
15686     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15687
15688     if (tape.playing && tape.deactivate_display)
15689       TapeDeactivateDisplayOn();
15690   }
15691 }
15692
15693 void RequestQuitGame(boolean escape_key_pressed)
15694 {
15695   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15696   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15697                         level_editor_test_game);
15698   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15699                           quick_quit);
15700
15701   RequestQuitGameExt(skip_request, quick_quit,
15702                      "Do you really want to quit the game?");
15703 }
15704
15705 void RequestRestartGame(char *message)
15706 {
15707   game.restart_game_message = NULL;
15708
15709   boolean has_started_game = hasStartedNetworkGame();
15710   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15711
15712   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15713   {
15714     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15715   }
15716   else
15717   {
15718     // needed in case of envelope request to close game panel
15719     CloseDoor(DOOR_CLOSE_1);
15720
15721     SetGameStatus(GAME_MODE_MAIN);
15722
15723     DrawMainMenu();
15724   }
15725 }
15726
15727 void CheckGameOver(void)
15728 {
15729   static boolean last_game_over = FALSE;
15730   static int game_over_delay = 0;
15731   int game_over_delay_value = 50;
15732   boolean game_over = checkGameFailed();
15733
15734   // do not handle game over if request dialog is already active
15735   if (game.request_active)
15736     return;
15737
15738   // do not ask to play again if game was never actually played
15739   if (!game.GamePlayed)
15740     return;
15741
15742   if (!game_over)
15743   {
15744     last_game_over = FALSE;
15745     game_over_delay = game_over_delay_value;
15746
15747     return;
15748   }
15749
15750   if (game_over_delay > 0)
15751   {
15752     game_over_delay--;
15753
15754     return;
15755   }
15756
15757   if (last_game_over != game_over)
15758     game.restart_game_message = (hasStartedNetworkGame() ?
15759                                  "Game over! Play it again?" :
15760                                  "Game over!");
15761
15762   last_game_over = game_over;
15763 }
15764
15765 boolean checkGameSolved(void)
15766 {
15767   // set for all game engines if level was solved
15768   return game.LevelSolved_GameEnd;
15769 }
15770
15771 boolean checkGameFailed(void)
15772 {
15773   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15774     return (game_em.game_over && !game_em.level_solved);
15775   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15776     return (game_sp.game_over && !game_sp.level_solved);
15777   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15778     return (game_mm.game_over && !game_mm.level_solved);
15779   else                          // GAME_ENGINE_TYPE_RND
15780     return (game.GameOver && !game.LevelSolved);
15781 }
15782
15783 boolean checkGameEnded(void)
15784 {
15785   return (checkGameSolved() || checkGameFailed());
15786 }
15787
15788
15789 // ----------------------------------------------------------------------------
15790 // random generator functions
15791 // ----------------------------------------------------------------------------
15792
15793 unsigned int InitEngineRandom_RND(int seed)
15794 {
15795   game.num_random_calls = 0;
15796
15797   return InitEngineRandom(seed);
15798 }
15799
15800 unsigned int RND(int max)
15801 {
15802   if (max > 0)
15803   {
15804     game.num_random_calls++;
15805
15806     return GetEngineRandom(max);
15807   }
15808
15809   return 0;
15810 }
15811
15812
15813 // ----------------------------------------------------------------------------
15814 // game engine snapshot handling functions
15815 // ----------------------------------------------------------------------------
15816
15817 struct EngineSnapshotInfo
15818 {
15819   // runtime values for custom element collect score
15820   int collect_score[NUM_CUSTOM_ELEMENTS];
15821
15822   // runtime values for group element choice position
15823   int choice_pos[NUM_GROUP_ELEMENTS];
15824
15825   // runtime values for belt position animations
15826   int belt_graphic[4][NUM_BELT_PARTS];
15827   int belt_anim_mode[4][NUM_BELT_PARTS];
15828 };
15829
15830 static struct EngineSnapshotInfo engine_snapshot_rnd;
15831 static char *snapshot_level_identifier = NULL;
15832 static int snapshot_level_nr = -1;
15833
15834 static void SaveEngineSnapshotValues_RND(void)
15835 {
15836   static int belt_base_active_element[4] =
15837   {
15838     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15839     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15840     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15841     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15842   };
15843   int i, j;
15844
15845   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15846   {
15847     int element = EL_CUSTOM_START + i;
15848
15849     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15850   }
15851
15852   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15853   {
15854     int element = EL_GROUP_START + i;
15855
15856     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15857   }
15858
15859   for (i = 0; i < 4; i++)
15860   {
15861     for (j = 0; j < NUM_BELT_PARTS; j++)
15862     {
15863       int element = belt_base_active_element[i] + j;
15864       int graphic = el2img(element);
15865       int anim_mode = graphic_info[graphic].anim_mode;
15866
15867       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15868       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15869     }
15870   }
15871 }
15872
15873 static void LoadEngineSnapshotValues_RND(void)
15874 {
15875   unsigned int num_random_calls = game.num_random_calls;
15876   int i, j;
15877
15878   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15879   {
15880     int element = EL_CUSTOM_START + i;
15881
15882     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15883   }
15884
15885   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15886   {
15887     int element = EL_GROUP_START + i;
15888
15889     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15890   }
15891
15892   for (i = 0; i < 4; i++)
15893   {
15894     for (j = 0; j < NUM_BELT_PARTS; j++)
15895     {
15896       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15897       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15898
15899       graphic_info[graphic].anim_mode = anim_mode;
15900     }
15901   }
15902
15903   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15904   {
15905     InitRND(tape.random_seed);
15906     for (i = 0; i < num_random_calls; i++)
15907       RND(1);
15908   }
15909
15910   if (game.num_random_calls != num_random_calls)
15911   {
15912     Error("number of random calls out of sync");
15913     Error("number of random calls should be %d", num_random_calls);
15914     Error("number of random calls is %d", game.num_random_calls);
15915
15916     Fail("this should not happen -- please debug");
15917   }
15918 }
15919
15920 void FreeEngineSnapshotSingle(void)
15921 {
15922   FreeSnapshotSingle();
15923
15924   setString(&snapshot_level_identifier, NULL);
15925   snapshot_level_nr = -1;
15926 }
15927
15928 void FreeEngineSnapshotList(void)
15929 {
15930   FreeSnapshotList();
15931 }
15932
15933 static ListNode *SaveEngineSnapshotBuffers(void)
15934 {
15935   ListNode *buffers = NULL;
15936
15937   // copy some special values to a structure better suited for the snapshot
15938
15939   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15940     SaveEngineSnapshotValues_RND();
15941   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15942     SaveEngineSnapshotValues_EM();
15943   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15944     SaveEngineSnapshotValues_SP(&buffers);
15945   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15946     SaveEngineSnapshotValues_MM(&buffers);
15947
15948   // save values stored in special snapshot structure
15949
15950   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15951     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15952   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15953     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15954   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15955     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15956   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15957     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15958
15959   // save further RND engine values
15960
15961   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15962   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15963   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15964
15965   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15966   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15967   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15968   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15969   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15970
15971   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15972   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15973   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15974
15975   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15976
15977   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15978   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15979
15980   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15981   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15982   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15983   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15984   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15985   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15986   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15987   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15988   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15989   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15990   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15991   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15992   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15993   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15994   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15995   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15996   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15997   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15998
15999   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16000   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16001
16002   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16003   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16004   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16005
16006   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16007   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16008
16009   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16010   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16011   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16012   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16013   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16014   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16015
16016   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16017   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16018
16019 #if 0
16020   ListNode *node = engine_snapshot_list_rnd;
16021   int num_bytes = 0;
16022
16023   while (node != NULL)
16024   {
16025     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16026
16027     node = node->next;
16028   }
16029
16030   Debug("game:playing:SaveEngineSnapshotBuffers",
16031         "size of engine snapshot: %d bytes", num_bytes);
16032 #endif
16033
16034   return buffers;
16035 }
16036
16037 void SaveEngineSnapshotSingle(void)
16038 {
16039   ListNode *buffers = SaveEngineSnapshotBuffers();
16040
16041   // finally save all snapshot buffers to single snapshot
16042   SaveSnapshotSingle(buffers);
16043
16044   // save level identification information
16045   setString(&snapshot_level_identifier, leveldir_current->identifier);
16046   snapshot_level_nr = level_nr;
16047 }
16048
16049 boolean CheckSaveEngineSnapshotToList(void)
16050 {
16051   boolean save_snapshot =
16052     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16053      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16054       game.snapshot.changed_action) ||
16055      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16056       game.snapshot.collected_item));
16057
16058   game.snapshot.changed_action = FALSE;
16059   game.snapshot.collected_item = FALSE;
16060   game.snapshot.save_snapshot = save_snapshot;
16061
16062   return save_snapshot;
16063 }
16064
16065 void SaveEngineSnapshotToList(void)
16066 {
16067   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16068       tape.quick_resume)
16069     return;
16070
16071   ListNode *buffers = SaveEngineSnapshotBuffers();
16072
16073   // finally save all snapshot buffers to snapshot list
16074   SaveSnapshotToList(buffers);
16075 }
16076
16077 void SaveEngineSnapshotToListInitial(void)
16078 {
16079   FreeEngineSnapshotList();
16080
16081   SaveEngineSnapshotToList();
16082 }
16083
16084 static void LoadEngineSnapshotValues(void)
16085 {
16086   // restore special values from snapshot structure
16087
16088   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16089     LoadEngineSnapshotValues_RND();
16090   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16091     LoadEngineSnapshotValues_EM();
16092   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16093     LoadEngineSnapshotValues_SP();
16094   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16095     LoadEngineSnapshotValues_MM();
16096 }
16097
16098 void LoadEngineSnapshotSingle(void)
16099 {
16100   LoadSnapshotSingle();
16101
16102   LoadEngineSnapshotValues();
16103 }
16104
16105 static void LoadEngineSnapshot_Undo(int steps)
16106 {
16107   LoadSnapshotFromList_Older(steps);
16108
16109   LoadEngineSnapshotValues();
16110 }
16111
16112 static void LoadEngineSnapshot_Redo(int steps)
16113 {
16114   LoadSnapshotFromList_Newer(steps);
16115
16116   LoadEngineSnapshotValues();
16117 }
16118
16119 boolean CheckEngineSnapshotSingle(void)
16120 {
16121   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16122           snapshot_level_nr == level_nr);
16123 }
16124
16125 boolean CheckEngineSnapshotList(void)
16126 {
16127   return CheckSnapshotList();
16128 }
16129
16130
16131 // ---------- new game button stuff -------------------------------------------
16132
16133 static struct
16134 {
16135   int graphic;
16136   struct XY *pos;
16137   int gadget_id;
16138   boolean *setup_value;
16139   boolean allowed_on_tape;
16140   boolean is_touch_button;
16141   char *infotext;
16142 } gamebutton_info[NUM_GAME_BUTTONS] =
16143 {
16144   {
16145     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16146     GAME_CTRL_ID_STOP,                          NULL,
16147     TRUE, FALSE,                                "stop game"
16148   },
16149   {
16150     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16151     GAME_CTRL_ID_PAUSE,                         NULL,
16152     TRUE, FALSE,                                "pause game"
16153   },
16154   {
16155     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16156     GAME_CTRL_ID_PLAY,                          NULL,
16157     TRUE, FALSE,                                "play game"
16158   },
16159   {
16160     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16161     GAME_CTRL_ID_UNDO,                          NULL,
16162     TRUE, FALSE,                                "undo step"
16163   },
16164   {
16165     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16166     GAME_CTRL_ID_REDO,                          NULL,
16167     TRUE, FALSE,                                "redo step"
16168   },
16169   {
16170     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16171     GAME_CTRL_ID_SAVE,                          NULL,
16172     TRUE, FALSE,                                "save game"
16173   },
16174   {
16175     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16176     GAME_CTRL_ID_PAUSE2,                        NULL,
16177     TRUE, FALSE,                                "pause game"
16178   },
16179   {
16180     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16181     GAME_CTRL_ID_LOAD,                          NULL,
16182     TRUE, FALSE,                                "load game"
16183   },
16184   {
16185     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16186     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16187     FALSE, FALSE,                               "stop game"
16188   },
16189   {
16190     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16191     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16192     FALSE, FALSE,                               "pause game"
16193   },
16194   {
16195     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16196     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16197     FALSE, FALSE,                               "play game"
16198   },
16199   {
16200     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16201     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16202     FALSE, TRUE,                                "stop game"
16203   },
16204   {
16205     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16206     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16207     FALSE, TRUE,                                "pause game"
16208   },
16209   {
16210     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16211     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16212     TRUE, FALSE,                                "background music on/off"
16213   },
16214   {
16215     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16216     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16217     TRUE, FALSE,                                "sound loops on/off"
16218   },
16219   {
16220     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16221     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16222     TRUE, FALSE,                                "normal sounds on/off"
16223   },
16224   {
16225     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16226     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16227     FALSE, FALSE,                               "background music on/off"
16228   },
16229   {
16230     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16231     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16232     FALSE, FALSE,                               "sound loops on/off"
16233   },
16234   {
16235     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16236     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16237     FALSE, FALSE,                               "normal sounds on/off"
16238   }
16239 };
16240
16241 void CreateGameButtons(void)
16242 {
16243   int i;
16244
16245   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16246   {
16247     int graphic = gamebutton_info[i].graphic;
16248     struct GraphicInfo *gfx = &graphic_info[graphic];
16249     struct XY *pos = gamebutton_info[i].pos;
16250     struct GadgetInfo *gi;
16251     int button_type;
16252     boolean checked;
16253     unsigned int event_mask;
16254     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16255     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16256     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16257     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16258     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16259     int gd_x   = gfx->src_x;
16260     int gd_y   = gfx->src_y;
16261     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16262     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16263     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16264     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16265     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16266     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16267     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16268     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16269     int id = i;
16270
16271     if (gfx->bitmap == NULL)
16272     {
16273       game_gadget[id] = NULL;
16274
16275       continue;
16276     }
16277
16278     if (id == GAME_CTRL_ID_STOP ||
16279         id == GAME_CTRL_ID_PANEL_STOP ||
16280         id == GAME_CTRL_ID_TOUCH_STOP ||
16281         id == GAME_CTRL_ID_PLAY ||
16282         id == GAME_CTRL_ID_PANEL_PLAY ||
16283         id == GAME_CTRL_ID_SAVE ||
16284         id == GAME_CTRL_ID_LOAD)
16285     {
16286       button_type = GD_TYPE_NORMAL_BUTTON;
16287       checked = FALSE;
16288       event_mask = GD_EVENT_RELEASED;
16289     }
16290     else if (id == GAME_CTRL_ID_UNDO ||
16291              id == GAME_CTRL_ID_REDO)
16292     {
16293       button_type = GD_TYPE_NORMAL_BUTTON;
16294       checked = FALSE;
16295       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16296     }
16297     else
16298     {
16299       button_type = GD_TYPE_CHECK_BUTTON;
16300       checked = (gamebutton_info[i].setup_value != NULL ?
16301                  *gamebutton_info[i].setup_value : FALSE);
16302       event_mask = GD_EVENT_PRESSED;
16303     }
16304
16305     gi = CreateGadget(GDI_CUSTOM_ID, id,
16306                       GDI_IMAGE_ID, graphic,
16307                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16308                       GDI_X, base_x + x,
16309                       GDI_Y, base_y + y,
16310                       GDI_WIDTH, gfx->width,
16311                       GDI_HEIGHT, gfx->height,
16312                       GDI_TYPE, button_type,
16313                       GDI_STATE, GD_BUTTON_UNPRESSED,
16314                       GDI_CHECKED, checked,
16315                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16316                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16317                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16318                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16319                       GDI_DIRECT_DRAW, FALSE,
16320                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16321                       GDI_EVENT_MASK, event_mask,
16322                       GDI_CALLBACK_ACTION, HandleGameButtons,
16323                       GDI_END);
16324
16325     if (gi == NULL)
16326       Fail("cannot create gadget");
16327
16328     game_gadget[id] = gi;
16329   }
16330 }
16331
16332 void FreeGameButtons(void)
16333 {
16334   int i;
16335
16336   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16337     FreeGadget(game_gadget[i]);
16338 }
16339
16340 static void UnmapGameButtonsAtSamePosition(int id)
16341 {
16342   int i;
16343
16344   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16345     if (i != id &&
16346         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16347         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16348       UnmapGadget(game_gadget[i]);
16349 }
16350
16351 static void UnmapGameButtonsAtSamePosition_All(void)
16352 {
16353   if (setup.show_load_save_buttons)
16354   {
16355     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16356     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16357     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16358   }
16359   else if (setup.show_undo_redo_buttons)
16360   {
16361     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16362     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16363     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16364   }
16365   else
16366   {
16367     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16368     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16369     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16370
16371     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16372     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16373     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16374   }
16375 }
16376
16377 void MapLoadSaveButtons(void)
16378 {
16379   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16380   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16381
16382   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16383   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16384 }
16385
16386 void MapUndoRedoButtons(void)
16387 {
16388   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16389   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16390
16391   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16392   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16393 }
16394
16395 void ModifyPauseButtons(void)
16396 {
16397   static int ids[] =
16398   {
16399     GAME_CTRL_ID_PAUSE,
16400     GAME_CTRL_ID_PAUSE2,
16401     GAME_CTRL_ID_PANEL_PAUSE,
16402     GAME_CTRL_ID_TOUCH_PAUSE,
16403     -1
16404   };
16405   int i;
16406
16407   for (i = 0; ids[i] > -1; i++)
16408     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16409 }
16410
16411 static void MapGameButtonsExt(boolean on_tape)
16412 {
16413   int i;
16414
16415   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16416     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16417       MapGadget(game_gadget[i]);
16418
16419   UnmapGameButtonsAtSamePosition_All();
16420
16421   RedrawGameButtons();
16422 }
16423
16424 static void UnmapGameButtonsExt(boolean on_tape)
16425 {
16426   int i;
16427
16428   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16429     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16430       UnmapGadget(game_gadget[i]);
16431 }
16432
16433 static void RedrawGameButtonsExt(boolean on_tape)
16434 {
16435   int i;
16436
16437   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16438     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16439       RedrawGadget(game_gadget[i]);
16440 }
16441
16442 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16443 {
16444   if (gi == NULL)
16445     return;
16446
16447   gi->checked = state;
16448 }
16449
16450 static void RedrawSoundButtonGadget(int id)
16451 {
16452   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16453              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16454              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16455              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16456              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16457              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16458              id);
16459
16460   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16461   RedrawGadget(game_gadget[id2]);
16462 }
16463
16464 void MapGameButtons(void)
16465 {
16466   MapGameButtonsExt(FALSE);
16467 }
16468
16469 void UnmapGameButtons(void)
16470 {
16471   UnmapGameButtonsExt(FALSE);
16472 }
16473
16474 void RedrawGameButtons(void)
16475 {
16476   RedrawGameButtonsExt(FALSE);
16477 }
16478
16479 void MapGameButtonsOnTape(void)
16480 {
16481   MapGameButtonsExt(TRUE);
16482 }
16483
16484 void UnmapGameButtonsOnTape(void)
16485 {
16486   UnmapGameButtonsExt(TRUE);
16487 }
16488
16489 void RedrawGameButtonsOnTape(void)
16490 {
16491   RedrawGameButtonsExt(TRUE);
16492 }
16493
16494 static void GameUndoRedoExt(void)
16495 {
16496   ClearPlayerAction();
16497
16498   tape.pausing = TRUE;
16499
16500   RedrawPlayfield();
16501   UpdateAndDisplayGameControlValues();
16502
16503   DrawCompleteVideoDisplay();
16504   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16505   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16506   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16507
16508   ModifyPauseButtons();
16509
16510   BackToFront();
16511 }
16512
16513 static void GameUndo(int steps)
16514 {
16515   if (!CheckEngineSnapshotList())
16516     return;
16517
16518   int tape_property_bits = tape.property_bits;
16519
16520   LoadEngineSnapshot_Undo(steps);
16521
16522   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16523
16524   GameUndoRedoExt();
16525 }
16526
16527 static void GameRedo(int steps)
16528 {
16529   if (!CheckEngineSnapshotList())
16530     return;
16531
16532   int tape_property_bits = tape.property_bits;
16533
16534   LoadEngineSnapshot_Redo(steps);
16535
16536   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16537
16538   GameUndoRedoExt();
16539 }
16540
16541 static void HandleGameButtonsExt(int id, int button)
16542 {
16543   static boolean game_undo_executed = FALSE;
16544   int steps = BUTTON_STEPSIZE(button);
16545   boolean handle_game_buttons =
16546     (game_status == GAME_MODE_PLAYING ||
16547      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16548
16549   if (!handle_game_buttons)
16550     return;
16551
16552   switch (id)
16553   {
16554     case GAME_CTRL_ID_STOP:
16555     case GAME_CTRL_ID_PANEL_STOP:
16556     case GAME_CTRL_ID_TOUCH_STOP:
16557       if (game_status == GAME_MODE_MAIN)
16558         break;
16559
16560       if (tape.playing)
16561         TapeStop();
16562       else
16563         RequestQuitGame(FALSE);
16564
16565       break;
16566
16567     case GAME_CTRL_ID_PAUSE:
16568     case GAME_CTRL_ID_PAUSE2:
16569     case GAME_CTRL_ID_PANEL_PAUSE:
16570     case GAME_CTRL_ID_TOUCH_PAUSE:
16571       if (network.enabled && game_status == GAME_MODE_PLAYING)
16572       {
16573         if (tape.pausing)
16574           SendToServer_ContinuePlaying();
16575         else
16576           SendToServer_PausePlaying();
16577       }
16578       else
16579         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16580
16581       game_undo_executed = FALSE;
16582
16583       break;
16584
16585     case GAME_CTRL_ID_PLAY:
16586     case GAME_CTRL_ID_PANEL_PLAY:
16587       if (game_status == GAME_MODE_MAIN)
16588       {
16589         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16590       }
16591       else if (tape.pausing)
16592       {
16593         if (network.enabled)
16594           SendToServer_ContinuePlaying();
16595         else
16596           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16597       }
16598       break;
16599
16600     case GAME_CTRL_ID_UNDO:
16601       // Important: When using "save snapshot when collecting an item" mode,
16602       // load last (current) snapshot for first "undo" after pressing "pause"
16603       // (else the last-but-one snapshot would be loaded, because the snapshot
16604       // pointer already points to the last snapshot when pressing "pause",
16605       // which is fine for "every step/move" mode, but not for "every collect")
16606       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16607           !game_undo_executed)
16608         steps--;
16609
16610       game_undo_executed = TRUE;
16611
16612       GameUndo(steps);
16613       break;
16614
16615     case GAME_CTRL_ID_REDO:
16616       GameRedo(steps);
16617       break;
16618
16619     case GAME_CTRL_ID_SAVE:
16620       TapeQuickSave();
16621       break;
16622
16623     case GAME_CTRL_ID_LOAD:
16624       TapeQuickLoad();
16625       break;
16626
16627     case SOUND_CTRL_ID_MUSIC:
16628     case SOUND_CTRL_ID_PANEL_MUSIC:
16629       if (setup.sound_music)
16630       { 
16631         setup.sound_music = FALSE;
16632
16633         FadeMusic();
16634       }
16635       else if (audio.music_available)
16636       { 
16637         setup.sound = setup.sound_music = TRUE;
16638
16639         SetAudioMode(setup.sound);
16640
16641         if (game_status == GAME_MODE_PLAYING)
16642           PlayLevelMusic();
16643       }
16644
16645       RedrawSoundButtonGadget(id);
16646
16647       break;
16648
16649     case SOUND_CTRL_ID_LOOPS:
16650     case SOUND_CTRL_ID_PANEL_LOOPS:
16651       if (setup.sound_loops)
16652         setup.sound_loops = FALSE;
16653       else if (audio.loops_available)
16654       {
16655         setup.sound = setup.sound_loops = TRUE;
16656
16657         SetAudioMode(setup.sound);
16658       }
16659
16660       RedrawSoundButtonGadget(id);
16661
16662       break;
16663
16664     case SOUND_CTRL_ID_SIMPLE:
16665     case SOUND_CTRL_ID_PANEL_SIMPLE:
16666       if (setup.sound_simple)
16667         setup.sound_simple = FALSE;
16668       else if (audio.sound_available)
16669       {
16670         setup.sound = setup.sound_simple = TRUE;
16671
16672         SetAudioMode(setup.sound);
16673       }
16674
16675       RedrawSoundButtonGadget(id);
16676
16677       break;
16678
16679     default:
16680       break;
16681   }
16682 }
16683
16684 static void HandleGameButtons(struct GadgetInfo *gi)
16685 {
16686   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16687 }
16688
16689 void HandleSoundButtonKeys(Key key)
16690 {
16691   if (key == setup.shortcut.sound_simple)
16692     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16693   else if (key == setup.shortcut.sound_loops)
16694     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16695   else if (key == setup.shortcut.sound_music)
16696     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16697 }