d7b6ad3d164a7799ecb87671b5272ceb0274797d
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static struct XY xy_topdown[] =
1558 {
1559   {  0, -1 },
1560   { -1,  0 },
1561   { +1,  0 },
1562   {  0, +1 }
1563 };
1564
1565 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1566
1567 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1568 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1569 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1570                                  IS_JUST_CHANGING(x, y))
1571
1572 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1573
1574 // static variables for playfield scan mode (scanning forward or backward)
1575 static int playfield_scan_start_x = 0;
1576 static int playfield_scan_start_y = 0;
1577 static int playfield_scan_delta_x = 1;
1578 static int playfield_scan_delta_y = 1;
1579
1580 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1581                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1582                                      (y) += playfield_scan_delta_y)     \
1583                                 for ((x) = playfield_scan_start_x;      \
1584                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1585                                      (x) += playfield_scan_delta_x)
1586
1587 #ifdef DEBUG
1588 void DEBUG_SetMaximumDynamite(void)
1589 {
1590   int i;
1591
1592   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1593     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1594       local_player->inventory_element[local_player->inventory_size++] =
1595         EL_DYNAMITE;
1596 }
1597 #endif
1598
1599 static void InitPlayfieldScanModeVars(void)
1600 {
1601   if (game.use_reverse_scan_direction)
1602   {
1603     playfield_scan_start_x = lev_fieldx - 1;
1604     playfield_scan_start_y = lev_fieldy - 1;
1605
1606     playfield_scan_delta_x = -1;
1607     playfield_scan_delta_y = -1;
1608   }
1609   else
1610   {
1611     playfield_scan_start_x = 0;
1612     playfield_scan_start_y = 0;
1613
1614     playfield_scan_delta_x = 1;
1615     playfield_scan_delta_y = 1;
1616   }
1617 }
1618
1619 static void InitPlayfieldScanMode(int mode)
1620 {
1621   game.use_reverse_scan_direction =
1622     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1623
1624   InitPlayfieldScanModeVars();
1625 }
1626
1627 static int get_move_delay_from_stepsize(int move_stepsize)
1628 {
1629   move_stepsize =
1630     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1631
1632   // make sure that stepsize value is always a power of 2
1633   move_stepsize = (1 << log_2(move_stepsize));
1634
1635   return TILEX / move_stepsize;
1636 }
1637
1638 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1639                                boolean init_game)
1640 {
1641   int player_nr = player->index_nr;
1642   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1643   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1644
1645   // do no immediately change move delay -- the player might just be moving
1646   player->move_delay_value_next = move_delay;
1647
1648   // information if player can move must be set separately
1649   player->cannot_move = cannot_move;
1650
1651   if (init_game)
1652   {
1653     player->move_delay       = game.initial_move_delay[player_nr];
1654     player->move_delay_value = game.initial_move_delay_value[player_nr];
1655
1656     player->move_delay_value_next = -1;
1657
1658     player->move_delay_reset_counter = 0;
1659   }
1660 }
1661
1662 void GetPlayerConfig(void)
1663 {
1664   GameFrameDelay = setup.game_frame_delay;
1665
1666   if (!audio.sound_available)
1667     setup.sound_simple = FALSE;
1668
1669   if (!audio.loops_available)
1670     setup.sound_loops = FALSE;
1671
1672   if (!audio.music_available)
1673     setup.sound_music = FALSE;
1674
1675   if (!video.fullscreen_available)
1676     setup.fullscreen = FALSE;
1677
1678   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1679
1680   SetAudioMode(setup.sound);
1681 }
1682
1683 int GetElementFromGroupElement(int element)
1684 {
1685   if (IS_GROUP_ELEMENT(element))
1686   {
1687     struct ElementGroupInfo *group = element_info[element].group;
1688     int last_anim_random_frame = gfx.anim_random_frame;
1689     int element_pos;
1690
1691     if (group->choice_mode == ANIM_RANDOM)
1692       gfx.anim_random_frame = RND(group->num_elements_resolved);
1693
1694     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1695                                     group->choice_mode, 0,
1696                                     group->choice_pos);
1697
1698     if (group->choice_mode == ANIM_RANDOM)
1699       gfx.anim_random_frame = last_anim_random_frame;
1700
1701     group->choice_pos++;
1702
1703     element = group->element_resolved[element_pos];
1704   }
1705
1706   return element;
1707 }
1708
1709 static void IncrementSokobanFieldsNeeded(void)
1710 {
1711   if (level.sb_fields_needed)
1712     game.sokoban_fields_still_needed++;
1713 }
1714
1715 static void IncrementSokobanObjectsNeeded(void)
1716 {
1717   if (level.sb_objects_needed)
1718     game.sokoban_objects_still_needed++;
1719 }
1720
1721 static void DecrementSokobanFieldsNeeded(void)
1722 {
1723   if (game.sokoban_fields_still_needed > 0)
1724     game.sokoban_fields_still_needed--;
1725 }
1726
1727 static void DecrementSokobanObjectsNeeded(void)
1728 {
1729   if (game.sokoban_objects_still_needed > 0)
1730     game.sokoban_objects_still_needed--;
1731 }
1732
1733 static void InitPlayerField(int x, int y, int element, boolean init_game)
1734 {
1735   if (element == EL_SP_MURPHY)
1736   {
1737     if (init_game)
1738     {
1739       if (stored_player[0].present)
1740       {
1741         Tile[x][y] = EL_SP_MURPHY_CLONE;
1742
1743         return;
1744       }
1745       else
1746       {
1747         stored_player[0].initial_element = element;
1748         stored_player[0].use_murphy = TRUE;
1749
1750         if (!level.use_artwork_element[0])
1751           stored_player[0].artwork_element = EL_SP_MURPHY;
1752       }
1753
1754       Tile[x][y] = EL_PLAYER_1;
1755     }
1756   }
1757
1758   if (init_game)
1759   {
1760     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1761     int jx = player->jx, jy = player->jy;
1762
1763     player->present = TRUE;
1764
1765     player->block_last_field = (element == EL_SP_MURPHY ?
1766                                 level.sp_block_last_field :
1767                                 level.block_last_field);
1768
1769     // ---------- initialize player's last field block delay ------------------
1770
1771     // always start with reliable default value (no adjustment needed)
1772     player->block_delay_adjustment = 0;
1773
1774     // special case 1: in Supaplex, Murphy blocks last field one more frame
1775     if (player->block_last_field && element == EL_SP_MURPHY)
1776       player->block_delay_adjustment = 1;
1777
1778     // special case 2: in game engines before 3.1.1, blocking was different
1779     if (game.use_block_last_field_bug)
1780       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1781
1782     if (!network.enabled || player->connected_network)
1783     {
1784       player->active = TRUE;
1785
1786       // remove potentially duplicate players
1787       if (StorePlayer[jx][jy] == Tile[x][y])
1788         StorePlayer[jx][jy] = 0;
1789
1790       StorePlayer[x][y] = Tile[x][y];
1791
1792 #if DEBUG_INIT_PLAYER
1793       Debug("game:init:player", "- player element %d activated",
1794             player->element_nr);
1795       Debug("game:init:player", "  (local player is %d and currently %s)",
1796             local_player->element_nr,
1797             local_player->active ? "active" : "not active");
1798     }
1799 #endif
1800
1801     Tile[x][y] = EL_EMPTY;
1802
1803     player->jx = player->last_jx = x;
1804     player->jy = player->last_jy = y;
1805   }
1806
1807   // always check if player was just killed and should be reanimated
1808   {
1809     int player_nr = GET_PLAYER_NR(element);
1810     struct PlayerInfo *player = &stored_player[player_nr];
1811
1812     if (player->active && player->killed)
1813       player->reanimated = TRUE; // if player was just killed, reanimate him
1814   }
1815 }
1816
1817 static void InitField(int x, int y, boolean init_game)
1818 {
1819   int element = Tile[x][y];
1820
1821   switch (element)
1822   {
1823     case EL_SP_MURPHY:
1824     case EL_PLAYER_1:
1825     case EL_PLAYER_2:
1826     case EL_PLAYER_3:
1827     case EL_PLAYER_4:
1828       InitPlayerField(x, y, element, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_PLAYER:
1832       element = Tile[x][y] = EL_PLAYER_1;
1833       InitField(x, y, init_game);
1834
1835       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1836       InitField(x, y, init_game);
1837       break;
1838
1839     case EL_SOKOBAN_FIELD_EMPTY:
1840       IncrementSokobanFieldsNeeded();
1841       break;
1842
1843     case EL_SOKOBAN_OBJECT:
1844       IncrementSokobanObjectsNeeded();
1845       break;
1846
1847     case EL_STONEBLOCK:
1848       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1849         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1850       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1851         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1852       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1853         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1854       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1855         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1856       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1857         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1858       break;
1859
1860     case EL_BUG:
1861     case EL_BUG_RIGHT:
1862     case EL_BUG_UP:
1863     case EL_BUG_LEFT:
1864     case EL_BUG_DOWN:
1865     case EL_SPACESHIP:
1866     case EL_SPACESHIP_RIGHT:
1867     case EL_SPACESHIP_UP:
1868     case EL_SPACESHIP_LEFT:
1869     case EL_SPACESHIP_DOWN:
1870     case EL_BD_BUTTERFLY:
1871     case EL_BD_BUTTERFLY_RIGHT:
1872     case EL_BD_BUTTERFLY_UP:
1873     case EL_BD_BUTTERFLY_LEFT:
1874     case EL_BD_BUTTERFLY_DOWN:
1875     case EL_BD_FIREFLY:
1876     case EL_BD_FIREFLY_RIGHT:
1877     case EL_BD_FIREFLY_UP:
1878     case EL_BD_FIREFLY_LEFT:
1879     case EL_BD_FIREFLY_DOWN:
1880     case EL_PACMAN_RIGHT:
1881     case EL_PACMAN_UP:
1882     case EL_PACMAN_LEFT:
1883     case EL_PACMAN_DOWN:
1884     case EL_YAMYAM:
1885     case EL_YAMYAM_LEFT:
1886     case EL_YAMYAM_RIGHT:
1887     case EL_YAMYAM_UP:
1888     case EL_YAMYAM_DOWN:
1889     case EL_DARK_YAMYAM:
1890     case EL_ROBOT:
1891     case EL_PACMAN:
1892     case EL_SP_SNIKSNAK:
1893     case EL_SP_ELECTRON:
1894     case EL_MOLE:
1895     case EL_MOLE_LEFT:
1896     case EL_MOLE_RIGHT:
1897     case EL_MOLE_UP:
1898     case EL_MOLE_DOWN:
1899     case EL_SPRING_LEFT:
1900     case EL_SPRING_RIGHT:
1901       InitMovDir(x, y);
1902       break;
1903
1904     case EL_AMOEBA_FULL:
1905     case EL_BD_AMOEBA:
1906       InitAmoebaNr(x, y);
1907       break;
1908
1909     case EL_AMOEBA_DROP:
1910       if (y == lev_fieldy - 1)
1911       {
1912         Tile[x][y] = EL_AMOEBA_GROWING;
1913         Store[x][y] = EL_AMOEBA_WET;
1914       }
1915       break;
1916
1917     case EL_DYNAMITE_ACTIVE:
1918     case EL_SP_DISK_RED_ACTIVE:
1919     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1920     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1921     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1922     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1923       MovDelay[x][y] = 96;
1924       break;
1925
1926     case EL_EM_DYNAMITE_ACTIVE:
1927       MovDelay[x][y] = 32;
1928       break;
1929
1930     case EL_LAMP:
1931       game.lights_still_needed++;
1932       break;
1933
1934     case EL_PENGUIN:
1935       game.friends_still_needed++;
1936       break;
1937
1938     case EL_PIG:
1939     case EL_DRAGON:
1940       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1941       break;
1942
1943     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1944     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1945     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1946     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1947     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1948     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1949     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1950     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1951     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1952     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1953     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1954     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1955       if (init_game)
1956       {
1957         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1958         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1959         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1960
1961         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1962         {
1963           game.belt_dir[belt_nr] = belt_dir;
1964           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1965         }
1966         else    // more than one switch -- set it like the first switch
1967         {
1968           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1969         }
1970       }
1971       break;
1972
1973     case EL_LIGHT_SWITCH_ACTIVE:
1974       if (init_game)
1975         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1976       break;
1977
1978     case EL_INVISIBLE_STEELWALL:
1979     case EL_INVISIBLE_WALL:
1980     case EL_INVISIBLE_SAND:
1981       if (game.light_time_left > 0 ||
1982           game.lenses_time_left > 0)
1983         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1984       break;
1985
1986     case EL_EMC_MAGIC_BALL:
1987       if (game.ball_active)
1988         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1989       break;
1990
1991     case EL_EMC_MAGIC_BALL_SWITCH:
1992       if (game.ball_active)
1993         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1994       break;
1995
1996     case EL_TRIGGER_PLAYER:
1997     case EL_TRIGGER_ELEMENT:
1998     case EL_TRIGGER_CE_VALUE:
1999     case EL_TRIGGER_CE_SCORE:
2000     case EL_SELF:
2001     case EL_ANY_ELEMENT:
2002     case EL_CURRENT_CE_VALUE:
2003     case EL_CURRENT_CE_SCORE:
2004     case EL_PREV_CE_1:
2005     case EL_PREV_CE_2:
2006     case EL_PREV_CE_3:
2007     case EL_PREV_CE_4:
2008     case EL_PREV_CE_5:
2009     case EL_PREV_CE_6:
2010     case EL_PREV_CE_7:
2011     case EL_PREV_CE_8:
2012     case EL_NEXT_CE_1:
2013     case EL_NEXT_CE_2:
2014     case EL_NEXT_CE_3:
2015     case EL_NEXT_CE_4:
2016     case EL_NEXT_CE_5:
2017     case EL_NEXT_CE_6:
2018     case EL_NEXT_CE_7:
2019     case EL_NEXT_CE_8:
2020       // reference elements should not be used on the playfield
2021       Tile[x][y] = EL_EMPTY;
2022       break;
2023
2024     default:
2025       if (IS_CUSTOM_ELEMENT(element))
2026       {
2027         if (CAN_MOVE(element))
2028           InitMovDir(x, y);
2029
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2032       }
2033       else if (IS_GROUP_ELEMENT(element))
2034       {
2035         Tile[x][y] = GetElementFromGroupElement(element);
2036
2037         InitField(x, y, init_game);
2038       }
2039       else if (IS_EMPTY_ELEMENT(element))
2040       {
2041         GfxElementEmpty[x][y] = element;
2042         Tile[x][y] = EL_EMPTY;
2043
2044         if (element_info[element].use_gfx_element)
2045           game.use_masked_elements = TRUE;
2046       }
2047
2048       break;
2049   }
2050
2051   if (!init_game)
2052     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2053 }
2054
2055 static void InitField_WithBug1(int x, int y, boolean init_game)
2056 {
2057   InitField(x, y, init_game);
2058
2059   // not needed to call InitMovDir() -- already done by InitField()!
2060   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2061       CAN_MOVE(Tile[x][y]))
2062     InitMovDir(x, y);
2063 }
2064
2065 static void InitField_WithBug2(int x, int y, boolean init_game)
2066 {
2067   int old_element = Tile[x][y];
2068
2069   InitField(x, y, init_game);
2070
2071   // not needed to call InitMovDir() -- already done by InitField()!
2072   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2073       CAN_MOVE(old_element) &&
2074       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2075     InitMovDir(x, y);
2076
2077   /* this case is in fact a combination of not less than three bugs:
2078      first, it calls InitMovDir() for elements that can move, although this is
2079      already done by InitField(); then, it checks the element that was at this
2080      field _before_ the call to InitField() (which can change it); lastly, it
2081      was not called for "mole with direction" elements, which were treated as
2082      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2083   */
2084 }
2085
2086 static int get_key_element_from_nr(int key_nr)
2087 {
2088   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2089                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2090                           EL_EM_KEY_1 : EL_KEY_1);
2091
2092   return key_base_element + key_nr;
2093 }
2094
2095 static int get_next_dropped_element(struct PlayerInfo *player)
2096 {
2097   return (player->inventory_size > 0 ?
2098           player->inventory_element[player->inventory_size - 1] :
2099           player->inventory_infinite_element != EL_UNDEFINED ?
2100           player->inventory_infinite_element :
2101           player->dynabombs_left > 0 ?
2102           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2103           EL_UNDEFINED);
2104 }
2105
2106 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2107 {
2108   // pos >= 0: get element from bottom of the stack;
2109   // pos <  0: get element from top of the stack
2110
2111   if (pos < 0)
2112   {
2113     int min_inventory_size = -pos;
2114     int inventory_pos = player->inventory_size - min_inventory_size;
2115     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2116
2117     return (player->inventory_size >= min_inventory_size ?
2118             player->inventory_element[inventory_pos] :
2119             player->inventory_infinite_element != EL_UNDEFINED ?
2120             player->inventory_infinite_element :
2121             player->dynabombs_left >= min_dynabombs_left ?
2122             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2123             EL_UNDEFINED);
2124   }
2125   else
2126   {
2127     int min_dynabombs_left = pos + 1;
2128     int min_inventory_size = pos + 1 - player->dynabombs_left;
2129     int inventory_pos = pos - player->dynabombs_left;
2130
2131     return (player->inventory_infinite_element != EL_UNDEFINED ?
2132             player->inventory_infinite_element :
2133             player->dynabombs_left >= min_dynabombs_left ?
2134             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2135             player->inventory_size >= min_inventory_size ?
2136             player->inventory_element[inventory_pos] :
2137             EL_UNDEFINED);
2138   }
2139 }
2140
2141 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2142 {
2143   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2144   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2145   int compare_result;
2146
2147   if (gpo1->sort_priority != gpo2->sort_priority)
2148     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2149   else
2150     compare_result = gpo1->nr - gpo2->nr;
2151
2152   return compare_result;
2153 }
2154
2155 int getPlayerInventorySize(int player_nr)
2156 {
2157   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2158     return game_em.ply[player_nr]->dynamite;
2159   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2160     return game_sp.red_disk_count;
2161   else
2162     return stored_player[player_nr].inventory_size;
2163 }
2164
2165 static void InitGameControlValues(void)
2166 {
2167   int i;
2168
2169   for (i = 0; game_panel_controls[i].nr != -1; i++)
2170   {
2171     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2172     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2173     struct TextPosInfo *pos = gpc->pos;
2174     int nr = gpc->nr;
2175     int type = gpc->type;
2176
2177     if (nr != i)
2178     {
2179       Error("'game_panel_controls' structure corrupted at %d", i);
2180
2181       Fail("this should not happen -- please debug");
2182     }
2183
2184     // force update of game controls after initialization
2185     gpc->value = gpc->last_value = -1;
2186     gpc->frame = gpc->last_frame = -1;
2187     gpc->gfx_frame = -1;
2188
2189     // determine panel value width for later calculation of alignment
2190     if (type == TYPE_INTEGER || type == TYPE_STRING)
2191     {
2192       pos->width = pos->size * getFontWidth(pos->font);
2193       pos->height = getFontHeight(pos->font);
2194     }
2195     else if (type == TYPE_ELEMENT)
2196     {
2197       pos->width = pos->size;
2198       pos->height = pos->size;
2199     }
2200
2201     // fill structure for game panel draw order
2202     gpo->nr = gpc->nr;
2203     gpo->sort_priority = pos->sort_priority;
2204   }
2205
2206   // sort game panel controls according to sort_priority and control number
2207   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2208         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2209 }
2210
2211 static void UpdatePlayfieldElementCount(void)
2212 {
2213   boolean use_element_count = FALSE;
2214   int i, j, x, y;
2215
2216   // first check if it is needed at all to calculate playfield element count
2217   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2218     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2219       use_element_count = TRUE;
2220
2221   if (!use_element_count)
2222     return;
2223
2224   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2225     element_info[i].element_count = 0;
2226
2227   SCAN_PLAYFIELD(x, y)
2228   {
2229     element_info[Tile[x][y]].element_count++;
2230   }
2231
2232   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2233     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2234       if (IS_IN_GROUP(j, i))
2235         element_info[EL_GROUP_START + i].element_count +=
2236           element_info[j].element_count;
2237 }
2238
2239 static void UpdateGameControlValues(void)
2240 {
2241   int i, k;
2242   int time = (game.LevelSolved ?
2243               game.LevelSolved_CountingTime :
2244               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2245               game_em.lev->time :
2246               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2247               game_sp.time_played :
2248               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249               game_mm.energy_left :
2250               game.no_level_time_limit ? TimePlayed : TimeLeft);
2251   int score = (game.LevelSolved ?
2252                game.LevelSolved_CountingScore :
2253                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2254                game_em.lev->score :
2255                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2256                game_sp.score :
2257                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2258                game_mm.score :
2259                game.score);
2260   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2261               game_em.lev->gems_needed :
2262               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2263               game_sp.infotrons_still_needed :
2264               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2265               game_mm.kettles_still_needed :
2266               game.gems_still_needed);
2267   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2268                      game_em.lev->gems_needed > 0 :
2269                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2270                      game_sp.infotrons_still_needed > 0 :
2271                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2272                      game_mm.kettles_still_needed > 0 ||
2273                      game_mm.lights_still_needed > 0 :
2274                      game.gems_still_needed > 0 ||
2275                      game.sokoban_fields_still_needed > 0 ||
2276                      game.sokoban_objects_still_needed > 0 ||
2277                      game.lights_still_needed > 0);
2278   int health = (game.LevelSolved ?
2279                 game.LevelSolved_CountingHealth :
2280                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2281                 MM_HEALTH(game_mm.laser_overload_value) :
2282                 game.health);
2283   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2284
2285   UpdatePlayfieldElementCount();
2286
2287   // update game panel control values
2288
2289   // used instead of "level_nr" (for network games)
2290   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2291   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2292
2293   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2294   for (i = 0; i < MAX_NUM_KEYS; i++)
2295     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2296   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2297   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2298
2299   if (game.centered_player_nr == -1)
2300   {
2301     for (i = 0; i < MAX_PLAYERS; i++)
2302     {
2303       // only one player in Supaplex game engine
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2305         break;
2306
2307       for (k = 0; k < MAX_NUM_KEYS; k++)
2308       {
2309         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2310         {
2311           if (game_em.ply[i]->keys & (1 << k))
2312             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2313               get_key_element_from_nr(k);
2314         }
2315         else if (stored_player[i].key[k])
2316           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2317             get_key_element_from_nr(k);
2318       }
2319
2320       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2321         getPlayerInventorySize(i);
2322
2323       if (stored_player[i].num_white_keys > 0)
2324         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2325           EL_DC_KEY_WHITE;
2326
2327       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328         stored_player[i].num_white_keys;
2329     }
2330   }
2331   else
2332   {
2333     int player_nr = game.centered_player_nr;
2334
2335     for (k = 0; k < MAX_NUM_KEYS; k++)
2336     {
2337       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2338       {
2339         if (game_em.ply[player_nr]->keys & (1 << k))
2340           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2341             get_key_element_from_nr(k);
2342       }
2343       else if (stored_player[player_nr].key[k])
2344         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2345           get_key_element_from_nr(k);
2346     }
2347
2348     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2349       getPlayerInventorySize(player_nr);
2350
2351     if (stored_player[player_nr].num_white_keys > 0)
2352       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2353
2354     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2355       stored_player[player_nr].num_white_keys;
2356   }
2357
2358   // re-arrange keys on game panel, if needed or if defined by style settings
2359   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2360   {
2361     int nr = GAME_PANEL_KEY_1 + i;
2362     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2363     struct TextPosInfo *pos = gpc->pos;
2364
2365     // skip check if key is not in the player's inventory
2366     if (gpc->value == EL_EMPTY)
2367       continue;
2368
2369     // check if keys should be arranged on panel from left to right
2370     if (pos->style == STYLE_LEFTMOST_POSITION)
2371     {
2372       // check previous key positions (left from current key)
2373       for (k = 0; k < i; k++)
2374       {
2375         int nr_new = GAME_PANEL_KEY_1 + k;
2376
2377         if (game_panel_controls[nr_new].value == EL_EMPTY)
2378         {
2379           game_panel_controls[nr_new].value = gpc->value;
2380           gpc->value = EL_EMPTY;
2381
2382           break;
2383         }
2384       }
2385     }
2386
2387     // check if "undefined" keys can be placed at some other position
2388     if (pos->x == -1 && pos->y == -1)
2389     {
2390       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2391
2392       // 1st try: display key at the same position as normal or EM keys
2393       if (game_panel_controls[nr_new].value == EL_EMPTY)
2394       {
2395         game_panel_controls[nr_new].value = gpc->value;
2396       }
2397       else
2398       {
2399         // 2nd try: display key at the next free position in the key panel
2400         for (k = 0; k < STD_NUM_KEYS; k++)
2401         {
2402           nr_new = GAME_PANEL_KEY_1 + k;
2403
2404           if (game_panel_controls[nr_new].value == EL_EMPTY)
2405           {
2406             game_panel_controls[nr_new].value = gpc->value;
2407
2408             break;
2409           }
2410         }
2411       }
2412     }
2413   }
2414
2415   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2416   {
2417     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2418       get_inventory_element_from_pos(local_player, i);
2419     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2420       get_inventory_element_from_pos(local_player, -i - 1);
2421   }
2422
2423   game_panel_controls[GAME_PANEL_SCORE].value = score;
2424   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2425
2426   game_panel_controls[GAME_PANEL_TIME].value = time;
2427
2428   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2429   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2430   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2431
2432   if (level.time == 0)
2433     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2434   else
2435     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2436
2437   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2438   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2439
2440   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2441
2442   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2443     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2444      EL_EMPTY);
2445   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2446     local_player->shield_normal_time_left;
2447   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2448     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2449      EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2451     local_player->shield_deadly_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EXIT].value =
2454     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2455
2456   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2457     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2458   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2459     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2460      EL_EMC_MAGIC_BALL_SWITCH);
2461
2462   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2463     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2464   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2465     game.light_time_left;
2466
2467   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2468     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2469   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2470     game.timegate_time_left;
2471
2472   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2473     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2474
2475   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2476     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2477   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2478     game.lenses_time_left;
2479
2480   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2481     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2482   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2483     game.magnify_time_left;
2484
2485   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2486     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2487      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2488      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2489      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2490      EL_BALLOON_SWITCH_NONE);
2491
2492   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2493     local_player->dynabomb_count;
2494   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2495     local_player->dynabomb_size;
2496   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2497     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2498
2499   game_panel_controls[GAME_PANEL_PENGUINS].value =
2500     game.friends_still_needed;
2501
2502   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2503     game.sokoban_objects_still_needed;
2504   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2505     game.sokoban_fields_still_needed;
2506
2507   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2508     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2509
2510   for (i = 0; i < NUM_BELTS; i++)
2511   {
2512     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2513       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2514        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2515     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2516       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2517   }
2518
2519   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2520     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2521   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2522     game.magic_wall_time_left;
2523
2524   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2525     local_player->gravity;
2526
2527   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2528     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2529
2530   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2531     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2532       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2533        game.panel.element[i].id : EL_UNDEFINED);
2534
2535   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2536     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2537       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2538        element_info[game.panel.element_count[i].id].element_count : 0);
2539
2540   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2541     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2542       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2543        element_info[game.panel.ce_score[i].id].collect_score : 0);
2544
2545   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2546     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2547       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2548        element_info[game.panel.ce_score_element[i].id].collect_score :
2549        EL_UNDEFINED);
2550
2551   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2552   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2553   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2554
2555   // update game panel control frames
2556
2557   for (i = 0; game_panel_controls[i].nr != -1; i++)
2558   {
2559     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2560
2561     if (gpc->type == TYPE_ELEMENT)
2562     {
2563       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2564       {
2565         int last_anim_random_frame = gfx.anim_random_frame;
2566         int element = gpc->value;
2567         int graphic = el2panelimg(element);
2568         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2569                                sync_random_frame : INIT_GFX_RANDOM());
2570
2571         if (gpc->value != gpc->last_value)
2572         {
2573           gpc->gfx_frame = 0;
2574           gpc->gfx_random = init_gfx_random;
2575         }
2576         else
2577         {
2578           gpc->gfx_frame++;
2579
2580           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2581               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2582             gpc->gfx_random = init_gfx_random;
2583         }
2584
2585         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2586           gfx.anim_random_frame = gpc->gfx_random;
2587
2588         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2589           gpc->gfx_frame = element_info[element].collect_score;
2590
2591         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2592
2593         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2594           gfx.anim_random_frame = last_anim_random_frame;
2595       }
2596     }
2597     else if (gpc->type == TYPE_GRAPHIC)
2598     {
2599       if (gpc->graphic != IMG_UNDEFINED)
2600       {
2601         int last_anim_random_frame = gfx.anim_random_frame;
2602         int graphic = gpc->graphic;
2603         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2604                                sync_random_frame : INIT_GFX_RANDOM());
2605
2606         if (gpc->value != gpc->last_value)
2607         {
2608           gpc->gfx_frame = 0;
2609           gpc->gfx_random = init_gfx_random;
2610         }
2611         else
2612         {
2613           gpc->gfx_frame++;
2614
2615           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2616               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2617             gpc->gfx_random = init_gfx_random;
2618         }
2619
2620         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2621           gfx.anim_random_frame = gpc->gfx_random;
2622
2623         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2624
2625         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2626           gfx.anim_random_frame = last_anim_random_frame;
2627       }
2628     }
2629   }
2630 }
2631
2632 static void DisplayGameControlValues(void)
2633 {
2634   boolean redraw_panel = FALSE;
2635   int i;
2636
2637   for (i = 0; game_panel_controls[i].nr != -1; i++)
2638   {
2639     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2640
2641     if (PANEL_DEACTIVATED(gpc->pos))
2642       continue;
2643
2644     if (gpc->value == gpc->last_value &&
2645         gpc->frame == gpc->last_frame)
2646       continue;
2647
2648     redraw_panel = TRUE;
2649   }
2650
2651   if (!redraw_panel)
2652     return;
2653
2654   // copy default game door content to main double buffer
2655
2656   // !!! CHECK AGAIN !!!
2657   SetPanelBackground();
2658   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2659   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2660
2661   // redraw game control buttons
2662   RedrawGameButtons();
2663
2664   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2665
2666   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2667   {
2668     int nr = game_panel_order[i].nr;
2669     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2670     struct TextPosInfo *pos = gpc->pos;
2671     int type = gpc->type;
2672     int value = gpc->value;
2673     int frame = gpc->frame;
2674     int size = pos->size;
2675     int font = pos->font;
2676     boolean draw_masked = pos->draw_masked;
2677     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2678
2679     if (PANEL_DEACTIVATED(pos))
2680       continue;
2681
2682     if (pos->class == get_hash_from_key("extra_panel_items") &&
2683         !setup.prefer_extra_panel_items)
2684       continue;
2685
2686     gpc->last_value = value;
2687     gpc->last_frame = frame;
2688
2689     if (type == TYPE_INTEGER)
2690     {
2691       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2692           nr == GAME_PANEL_INVENTORY_COUNT ||
2693           nr == GAME_PANEL_SCORE ||
2694           nr == GAME_PANEL_HIGHSCORE ||
2695           nr == GAME_PANEL_TIME)
2696       {
2697         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2698
2699         if (use_dynamic_size)           // use dynamic number of digits
2700         {
2701           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2702                               nr == GAME_PANEL_INVENTORY_COUNT ||
2703                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2704           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2705                           nr == GAME_PANEL_INVENTORY_COUNT ||
2706                           nr == GAME_PANEL_TIME ? 1 : 2);
2707           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2708                        nr == GAME_PANEL_INVENTORY_COUNT ||
2709                        nr == GAME_PANEL_TIME ? 3 : 5);
2710           int size2 = size1 + size_add;
2711           int font1 = pos->font;
2712           int font2 = pos->font_alt;
2713
2714           size = (value < value_change ? size1 : size2);
2715           font = (value < value_change ? font1 : font2);
2716         }
2717       }
2718
2719       // correct text size if "digits" is zero or less
2720       if (size <= 0)
2721         size = strlen(int2str(value, size));
2722
2723       // dynamically correct text alignment
2724       pos->width = size * getFontWidth(font);
2725
2726       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2727                   int2str(value, size), font, mask_mode);
2728     }
2729     else if (type == TYPE_ELEMENT)
2730     {
2731       int element, graphic;
2732       Bitmap *src_bitmap;
2733       int src_x, src_y;
2734       int width, height;
2735       int dst_x = PANEL_XPOS(pos);
2736       int dst_y = PANEL_YPOS(pos);
2737
2738       if (value != EL_UNDEFINED && value != EL_EMPTY)
2739       {
2740         element = value;
2741         graphic = el2panelimg(value);
2742
2743 #if 0
2744         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2745               element, EL_NAME(element), size);
2746 #endif
2747
2748         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2749           size = TILESIZE;
2750
2751         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2752                               &src_x, &src_y);
2753
2754         width  = graphic_info[graphic].width  * size / TILESIZE;
2755         height = graphic_info[graphic].height * size / TILESIZE;
2756
2757         if (draw_masked)
2758           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2759                            dst_x, dst_y);
2760         else
2761           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2762                      dst_x, dst_y);
2763       }
2764     }
2765     else if (type == TYPE_GRAPHIC)
2766     {
2767       int graphic        = gpc->graphic;
2768       int graphic_active = gpc->graphic_active;
2769       Bitmap *src_bitmap;
2770       int src_x, src_y;
2771       int width, height;
2772       int dst_x = PANEL_XPOS(pos);
2773       int dst_y = PANEL_YPOS(pos);
2774       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2775                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2776
2777       if (graphic != IMG_UNDEFINED && !skip)
2778       {
2779         if (pos->style == STYLE_REVERSE)
2780           value = 100 - value;
2781
2782         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2783
2784         if (pos->direction & MV_HORIZONTAL)
2785         {
2786           width  = graphic_info[graphic_active].width * value / 100;
2787           height = graphic_info[graphic_active].height;
2788
2789           if (pos->direction == MV_LEFT)
2790           {
2791             src_x += graphic_info[graphic_active].width - width;
2792             dst_x += graphic_info[graphic_active].width - width;
2793           }
2794         }
2795         else
2796         {
2797           width  = graphic_info[graphic_active].width;
2798           height = graphic_info[graphic_active].height * value / 100;
2799
2800           if (pos->direction == MV_UP)
2801           {
2802             src_y += graphic_info[graphic_active].height - height;
2803             dst_y += graphic_info[graphic_active].height - height;
2804           }
2805         }
2806
2807         if (draw_masked)
2808           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2809                            dst_x, dst_y);
2810         else
2811           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2812                      dst_x, dst_y);
2813
2814         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2815
2816         if (pos->direction & MV_HORIZONTAL)
2817         {
2818           if (pos->direction == MV_RIGHT)
2819           {
2820             src_x += width;
2821             dst_x += width;
2822           }
2823           else
2824           {
2825             dst_x = PANEL_XPOS(pos);
2826           }
2827
2828           width = graphic_info[graphic].width - width;
2829         }
2830         else
2831         {
2832           if (pos->direction == MV_DOWN)
2833           {
2834             src_y += height;
2835             dst_y += height;
2836           }
2837           else
2838           {
2839             dst_y = PANEL_YPOS(pos);
2840           }
2841
2842           height = graphic_info[graphic].height - height;
2843         }
2844
2845         if (draw_masked)
2846           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2847                            dst_x, dst_y);
2848         else
2849           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2850                      dst_x, dst_y);
2851       }
2852     }
2853     else if (type == TYPE_STRING)
2854     {
2855       boolean active = (value != 0);
2856       char *state_normal = "off";
2857       char *state_active = "on";
2858       char *state = (active ? state_active : state_normal);
2859       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2860                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2861                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2862                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2863
2864       if (nr == GAME_PANEL_GRAVITY_STATE)
2865       {
2866         int font1 = pos->font;          // (used for normal state)
2867         int font2 = pos->font_alt;      // (used for active state)
2868
2869         font = (active ? font2 : font1);
2870       }
2871
2872       if (s != NULL)
2873       {
2874         char *s_cut;
2875
2876         if (size <= 0)
2877         {
2878           // don't truncate output if "chars" is zero or less
2879           size = strlen(s);
2880
2881           // dynamically correct text alignment
2882           pos->width = size * getFontWidth(font);
2883         }
2884
2885         s_cut = getStringCopyN(s, size);
2886
2887         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2888                     s_cut, font, mask_mode);
2889
2890         free(s_cut);
2891       }
2892     }
2893
2894     redraw_mask |= REDRAW_DOOR_1;
2895   }
2896
2897   SetGameStatus(GAME_MODE_PLAYING);
2898 }
2899
2900 void UpdateAndDisplayGameControlValues(void)
2901 {
2902   if (tape.deactivate_display)
2903     return;
2904
2905   UpdateGameControlValues();
2906   DisplayGameControlValues();
2907 }
2908
2909 void UpdateGameDoorValues(void)
2910 {
2911   UpdateGameControlValues();
2912 }
2913
2914 void DrawGameDoorValues(void)
2915 {
2916   DisplayGameControlValues();
2917 }
2918
2919
2920 // ============================================================================
2921 // InitGameEngine()
2922 // ----------------------------------------------------------------------------
2923 // initialize game engine due to level / tape version number
2924 // ============================================================================
2925
2926 static void InitGameEngine(void)
2927 {
2928   int i, j, k, l, x, y;
2929
2930   // set game engine from tape file when re-playing, else from level file
2931   game.engine_version = (tape.playing ? tape.engine_version :
2932                          level.game_version);
2933
2934   // set single or multi-player game mode (needed for re-playing tapes)
2935   game.team_mode = setup.team_mode;
2936
2937   if (tape.playing)
2938   {
2939     int num_players = 0;
2940
2941     for (i = 0; i < MAX_PLAYERS; i++)
2942       if (tape.player_participates[i])
2943         num_players++;
2944
2945     // multi-player tapes contain input data for more than one player
2946     game.team_mode = (num_players > 1);
2947   }
2948
2949 #if 0
2950   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2951         level.game_version);
2952   Debug("game:init:level", "          tape.file_version   == %06d",
2953         tape.file_version);
2954   Debug("game:init:level", "          tape.game_version   == %06d",
2955         tape.game_version);
2956   Debug("game:init:level", "          tape.engine_version == %06d",
2957         tape.engine_version);
2958   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2959         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2960 #endif
2961
2962   // --------------------------------------------------------------------------
2963   // set flags for bugs and changes according to active game engine version
2964   // --------------------------------------------------------------------------
2965
2966   /*
2967     Summary of bugfix:
2968     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2969
2970     Bug was introduced in version:
2971     2.0.1
2972
2973     Bug was fixed in version:
2974     4.2.0.0
2975
2976     Description:
2977     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2978     but the property "can fall" was missing, which caused some levels to be
2979     unsolvable. This was fixed in version 4.2.0.0.
2980
2981     Affected levels/tapes:
2982     An example for a tape that was fixed by this bugfix is tape 029 from the
2983     level set "rnd_sam_bateman".
2984     The wrong behaviour will still be used for all levels or tapes that were
2985     created/recorded with it. An example for this is tape 023 from the level
2986     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2987   */
2988
2989   boolean use_amoeba_dropping_cannot_fall_bug =
2990     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2991       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2992      (tape.playing &&
2993       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2994       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2995
2996   /*
2997     Summary of bugfix/change:
2998     Fixed move speed of elements entering or leaving magic wall.
2999
3000     Fixed/changed in version:
3001     2.0.1
3002
3003     Description:
3004     Before 2.0.1, move speed of elements entering or leaving magic wall was
3005     twice as fast as it is now.
3006     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3007
3008     Affected levels/tapes:
3009     The first condition is generally needed for all levels/tapes before version
3010     2.0.1, which might use the old behaviour before it was changed; known tapes
3011     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3012     The second condition is an exception from the above case and is needed for
3013     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3014     above, but before it was known that this change would break tapes like the
3015     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3016     although the engine version while recording maybe was before 2.0.1. There
3017     are a lot of tapes that are affected by this exception, like tape 006 from
3018     the level set "rnd_conor_mancone".
3019   */
3020
3021   boolean use_old_move_stepsize_for_magic_wall =
3022     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3023      !(tape.playing &&
3024        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3025        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3026
3027   /*
3028     Summary of bugfix/change:
3029     Fixed handling for custom elements that change when pushed by the player.
3030
3031     Fixed/changed in version:
3032     3.1.0
3033
3034     Description:
3035     Before 3.1.0, custom elements that "change when pushing" changed directly
3036     after the player started pushing them (until then handled in "DigField()").
3037     Since 3.1.0, these custom elements are not changed until the "pushing"
3038     move of the element is finished (now handled in "ContinueMoving()").
3039
3040     Affected levels/tapes:
3041     The first condition is generally needed for all levels/tapes before version
3042     3.1.0, which might use the old behaviour before it was changed; known tapes
3043     that are affected are some tapes from the level set "Walpurgis Gardens" by
3044     Jamie Cullen.
3045     The second condition is an exception from the above case and is needed for
3046     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3047     above (including some development versions of 3.1.0), but before it was
3048     known that this change would break tapes like the above and was fixed in
3049     3.1.1, so that the changed behaviour was active although the engine version
3050     while recording maybe was before 3.1.0. There is at least one tape that is
3051     affected by this exception, which is the tape for the one-level set "Bug
3052     Machine" by Juergen Bonhagen.
3053   */
3054
3055   game.use_change_when_pushing_bug =
3056     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3057      !(tape.playing &&
3058        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3059        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3060
3061   /*
3062     Summary of bugfix/change:
3063     Fixed handling for blocking the field the player leaves when moving.
3064
3065     Fixed/changed in version:
3066     3.1.1
3067
3068     Description:
3069     Before 3.1.1, when "block last field when moving" was enabled, the field
3070     the player is leaving when moving was blocked for the time of the move,
3071     and was directly unblocked afterwards. This resulted in the last field
3072     being blocked for exactly one less than the number of frames of one player
3073     move. Additionally, even when blocking was disabled, the last field was
3074     blocked for exactly one frame.
3075     Since 3.1.1, due to changes in player movement handling, the last field
3076     is not blocked at all when blocking is disabled. When blocking is enabled,
3077     the last field is blocked for exactly the number of frames of one player
3078     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3079     last field is blocked for exactly one more than the number of frames of
3080     one player move.
3081
3082     Affected levels/tapes:
3083     (!!! yet to be determined -- probably many !!!)
3084   */
3085
3086   game.use_block_last_field_bug =
3087     (game.engine_version < VERSION_IDENT(3,1,1,0));
3088
3089   /* various special flags and settings for native Emerald Mine game engine */
3090
3091   game_em.use_single_button =
3092     (game.engine_version > VERSION_IDENT(4,0,0,2));
3093
3094   game_em.use_snap_key_bug =
3095     (game.engine_version < VERSION_IDENT(4,0,1,0));
3096
3097   game_em.use_random_bug =
3098     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3099
3100   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3101
3102   game_em.use_old_explosions            = use_old_em_engine;
3103   game_em.use_old_android               = use_old_em_engine;
3104   game_em.use_old_push_elements         = use_old_em_engine;
3105   game_em.use_old_push_into_acid        = use_old_em_engine;
3106
3107   game_em.use_wrap_around               = !use_old_em_engine;
3108
3109   // --------------------------------------------------------------------------
3110
3111   // set maximal allowed number of custom element changes per game frame
3112   game.max_num_changes_per_frame = 1;
3113
3114   // default scan direction: scan playfield from top/left to bottom/right
3115   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3116
3117   // dynamically adjust element properties according to game engine version
3118   InitElementPropertiesEngine(game.engine_version);
3119
3120   // ---------- initialize special element properties -------------------------
3121
3122   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3123   if (use_amoeba_dropping_cannot_fall_bug)
3124     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3125
3126   // ---------- initialize player's initial move delay ------------------------
3127
3128   // dynamically adjust player properties according to level information
3129   for (i = 0; i < MAX_PLAYERS; i++)
3130     game.initial_move_delay_value[i] =
3131       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3132
3133   // dynamically adjust player properties according to game engine version
3134   for (i = 0; i < MAX_PLAYERS; i++)
3135     game.initial_move_delay[i] =
3136       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3137        game.initial_move_delay_value[i] : 0);
3138
3139   // ---------- initialize player's initial push delay ------------------------
3140
3141   // dynamically adjust player properties according to game engine version
3142   game.initial_push_delay_value =
3143     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3144
3145   // ---------- initialize changing elements ----------------------------------
3146
3147   // initialize changing elements information
3148   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3149   {
3150     struct ElementInfo *ei = &element_info[i];
3151
3152     // this pointer might have been changed in the level editor
3153     ei->change = &ei->change_page[0];
3154
3155     if (!IS_CUSTOM_ELEMENT(i))
3156     {
3157       ei->change->target_element = EL_EMPTY_SPACE;
3158       ei->change->delay_fixed = 0;
3159       ei->change->delay_random = 0;
3160       ei->change->delay_frames = 1;
3161     }
3162
3163     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3164     {
3165       ei->has_change_event[j] = FALSE;
3166
3167       ei->event_page_nr[j] = 0;
3168       ei->event_page[j] = &ei->change_page[0];
3169     }
3170   }
3171
3172   // add changing elements from pre-defined list
3173   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3174   {
3175     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3176     struct ElementInfo *ei = &element_info[ch_delay->element];
3177
3178     ei->change->target_element       = ch_delay->target_element;
3179     ei->change->delay_fixed          = ch_delay->change_delay;
3180
3181     ei->change->pre_change_function  = ch_delay->pre_change_function;
3182     ei->change->change_function      = ch_delay->change_function;
3183     ei->change->post_change_function = ch_delay->post_change_function;
3184
3185     ei->change->can_change = TRUE;
3186     ei->change->can_change_or_has_action = TRUE;
3187
3188     ei->has_change_event[CE_DELAY] = TRUE;
3189
3190     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3191     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3192   }
3193
3194   // ---------- initialize internal run-time variables ------------------------
3195
3196   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3197   {
3198     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3199
3200     for (j = 0; j < ei->num_change_pages; j++)
3201     {
3202       ei->change_page[j].can_change_or_has_action =
3203         (ei->change_page[j].can_change |
3204          ei->change_page[j].has_action);
3205     }
3206   }
3207
3208   // add change events from custom element configuration
3209   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3210   {
3211     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3212
3213     for (j = 0; j < ei->num_change_pages; j++)
3214     {
3215       if (!ei->change_page[j].can_change_or_has_action)
3216         continue;
3217
3218       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3219       {
3220         // only add event page for the first page found with this event
3221         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3222         {
3223           ei->has_change_event[k] = TRUE;
3224
3225           ei->event_page_nr[k] = j;
3226           ei->event_page[k] = &ei->change_page[j];
3227         }
3228       }
3229     }
3230   }
3231
3232   // ---------- initialize reference elements in change conditions ------------
3233
3234   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3235   {
3236     int element = EL_CUSTOM_START + i;
3237     struct ElementInfo *ei = &element_info[element];
3238
3239     for (j = 0; j < ei->num_change_pages; j++)
3240     {
3241       int trigger_element = ei->change_page[j].initial_trigger_element;
3242
3243       if (trigger_element >= EL_PREV_CE_8 &&
3244           trigger_element <= EL_NEXT_CE_8)
3245         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3246
3247       ei->change_page[j].trigger_element = trigger_element;
3248     }
3249   }
3250
3251   // ---------- initialize run-time trigger player and element ----------------
3252
3253   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3254   {
3255     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3256
3257     for (j = 0; j < ei->num_change_pages; j++)
3258     {
3259       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3260       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3261       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3262       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3263       ei->change_page[j].actual_trigger_ce_value = 0;
3264       ei->change_page[j].actual_trigger_ce_score = 0;
3265     }
3266   }
3267
3268   // ---------- initialize trigger events -------------------------------------
3269
3270   // initialize trigger events information
3271   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3272     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3273       trigger_events[i][j] = FALSE;
3274
3275   // add trigger events from element change event properties
3276   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3277   {
3278     struct ElementInfo *ei = &element_info[i];
3279
3280     for (j = 0; j < ei->num_change_pages; j++)
3281     {
3282       if (!ei->change_page[j].can_change_or_has_action)
3283         continue;
3284
3285       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3286       {
3287         int trigger_element = ei->change_page[j].trigger_element;
3288
3289         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3290         {
3291           if (ei->change_page[j].has_event[k])
3292           {
3293             if (IS_GROUP_ELEMENT(trigger_element))
3294             {
3295               struct ElementGroupInfo *group =
3296                 element_info[trigger_element].group;
3297
3298               for (l = 0; l < group->num_elements_resolved; l++)
3299                 trigger_events[group->element_resolved[l]][k] = TRUE;
3300             }
3301             else if (trigger_element == EL_ANY_ELEMENT)
3302               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3303                 trigger_events[l][k] = TRUE;
3304             else
3305               trigger_events[trigger_element][k] = TRUE;
3306           }
3307         }
3308       }
3309     }
3310   }
3311
3312   // ---------- initialize push delay -----------------------------------------
3313
3314   // initialize push delay values to default
3315   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3316   {
3317     if (!IS_CUSTOM_ELEMENT(i))
3318     {
3319       // set default push delay values (corrected since version 3.0.7-1)
3320       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3321       {
3322         element_info[i].push_delay_fixed = 2;
3323         element_info[i].push_delay_random = 8;
3324       }
3325       else
3326       {
3327         element_info[i].push_delay_fixed = 8;
3328         element_info[i].push_delay_random = 8;
3329       }
3330     }
3331   }
3332
3333   // set push delay value for certain elements from pre-defined list
3334   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3335   {
3336     int e = push_delay_list[i].element;
3337
3338     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3339     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3340   }
3341
3342   // set push delay value for Supaplex elements for newer engine versions
3343   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3344   {
3345     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3346     {
3347       if (IS_SP_ELEMENT(i))
3348       {
3349         // set SP push delay to just enough to push under a falling zonk
3350         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3351
3352         element_info[i].push_delay_fixed  = delay;
3353         element_info[i].push_delay_random = 0;
3354       }
3355     }
3356   }
3357
3358   // ---------- initialize move stepsize --------------------------------------
3359
3360   // initialize move stepsize values to default
3361   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3362     if (!IS_CUSTOM_ELEMENT(i))
3363       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3364
3365   // set move stepsize value for certain elements from pre-defined list
3366   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3367   {
3368     int e = move_stepsize_list[i].element;
3369
3370     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3371
3372     // set move stepsize value for certain elements for older engine versions
3373     if (use_old_move_stepsize_for_magic_wall)
3374     {
3375       if (e == EL_MAGIC_WALL_FILLING ||
3376           e == EL_MAGIC_WALL_EMPTYING ||
3377           e == EL_BD_MAGIC_WALL_FILLING ||
3378           e == EL_BD_MAGIC_WALL_EMPTYING)
3379         element_info[e].move_stepsize *= 2;
3380     }
3381   }
3382
3383   // ---------- initialize collect score --------------------------------------
3384
3385   // initialize collect score values for custom elements from initial value
3386   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3387     if (IS_CUSTOM_ELEMENT(i))
3388       element_info[i].collect_score = element_info[i].collect_score_initial;
3389
3390   // ---------- initialize collect count --------------------------------------
3391
3392   // initialize collect count values for non-custom elements
3393   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3394     if (!IS_CUSTOM_ELEMENT(i))
3395       element_info[i].collect_count_initial = 0;
3396
3397   // add collect count values for all elements from pre-defined list
3398   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3399     element_info[collect_count_list[i].element].collect_count_initial =
3400       collect_count_list[i].count;
3401
3402   // ---------- initialize access direction -----------------------------------
3403
3404   // initialize access direction values to default (access from every side)
3405   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3406     if (!IS_CUSTOM_ELEMENT(i))
3407       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3408
3409   // set access direction value for certain elements from pre-defined list
3410   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3411     element_info[access_direction_list[i].element].access_direction =
3412       access_direction_list[i].direction;
3413
3414   // ---------- initialize explosion content ----------------------------------
3415   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3416   {
3417     if (IS_CUSTOM_ELEMENT(i))
3418       continue;
3419
3420     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3421     {
3422       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3423
3424       element_info[i].content.e[x][y] =
3425         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3426          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3427          i == EL_PLAYER_3 ? EL_EMERALD :
3428          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3429          i == EL_MOLE ? EL_EMERALD_RED :
3430          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3431          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3432          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3433          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3434          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3435          i == EL_WALL_EMERALD ? EL_EMERALD :
3436          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3437          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3438          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3439          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3440          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3441          i == EL_WALL_PEARL ? EL_PEARL :
3442          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3443          EL_EMPTY);
3444     }
3445   }
3446
3447   // ---------- initialize recursion detection --------------------------------
3448   recursion_loop_depth = 0;
3449   recursion_loop_detected = FALSE;
3450   recursion_loop_element = EL_UNDEFINED;
3451
3452   // ---------- initialize graphics engine ------------------------------------
3453   game.scroll_delay_value =
3454     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3455      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3456      !setup.forced_scroll_delay           ? 0 :
3457      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3458   game.scroll_delay_value =
3459     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3460
3461   // ---------- initialize game engine snapshots ------------------------------
3462   for (i = 0; i < MAX_PLAYERS; i++)
3463     game.snapshot.last_action[i] = 0;
3464   game.snapshot.changed_action = FALSE;
3465   game.snapshot.collected_item = FALSE;
3466   game.snapshot.mode =
3467     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3468      SNAPSHOT_MODE_EVERY_STEP :
3469      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3470      SNAPSHOT_MODE_EVERY_MOVE :
3471      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3472      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3473   game.snapshot.save_snapshot = FALSE;
3474
3475   // ---------- initialize level time for Supaplex engine ---------------------
3476   // Supaplex levels with time limit currently unsupported -- should be added
3477   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3478     level.time = 0;
3479
3480   // ---------- initialize flags for handling game actions --------------------
3481
3482   // set flags for game actions to default values
3483   game.use_key_actions = TRUE;
3484   game.use_mouse_actions = FALSE;
3485
3486   // when using Mirror Magic game engine, handle mouse events only
3487   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3488   {
3489     game.use_key_actions = FALSE;
3490     game.use_mouse_actions = TRUE;
3491   }
3492
3493   // check for custom elements with mouse click events
3494   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3495   {
3496     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3497     {
3498       int element = EL_CUSTOM_START + i;
3499
3500       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3501           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3502           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3503           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3504         game.use_mouse_actions = TRUE;
3505     }
3506   }
3507 }
3508
3509 static int get_num_special_action(int element, int action_first,
3510                                   int action_last)
3511 {
3512   int num_special_action = 0;
3513   int i, j;
3514
3515   for (i = action_first; i <= action_last; i++)
3516   {
3517     boolean found = FALSE;
3518
3519     for (j = 0; j < NUM_DIRECTIONS; j++)
3520       if (el_act_dir2img(element, i, j) !=
3521           el_act_dir2img(element, ACTION_DEFAULT, j))
3522         found = TRUE;
3523
3524     if (found)
3525       num_special_action++;
3526     else
3527       break;
3528   }
3529
3530   return num_special_action;
3531 }
3532
3533
3534 // ============================================================================
3535 // InitGame()
3536 // ----------------------------------------------------------------------------
3537 // initialize and start new game
3538 // ============================================================================
3539
3540 #if DEBUG_INIT_PLAYER
3541 static void DebugPrintPlayerStatus(char *message)
3542 {
3543   int i;
3544
3545   if (!options.debug)
3546     return;
3547
3548   Debug("game:init:player", "%s:", message);
3549
3550   for (i = 0; i < MAX_PLAYERS; i++)
3551   {
3552     struct PlayerInfo *player = &stored_player[i];
3553
3554     Debug("game:init:player",
3555           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3556           i + 1,
3557           player->present,
3558           player->connected,
3559           player->connected_locally,
3560           player->connected_network,
3561           player->active,
3562           (local_player == player ? " (local player)" : ""));
3563   }
3564 }
3565 #endif
3566
3567 void InitGame(void)
3568 {
3569   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3570   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3571   int fade_mask = REDRAW_FIELD;
3572
3573   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3574   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3575   int initial_move_dir = MV_DOWN;
3576   int i, j, x, y;
3577
3578   // required here to update video display before fading (FIX THIS)
3579   DrawMaskedBorder(REDRAW_DOOR_2);
3580
3581   if (!game.restart_level)
3582     CloseDoor(DOOR_CLOSE_1);
3583
3584   SetGameStatus(GAME_MODE_PLAYING);
3585
3586   if (level_editor_test_game)
3587     FadeSkipNextFadeOut();
3588   else
3589     FadeSetEnterScreen();
3590
3591   if (CheckFadeAll())
3592     fade_mask = REDRAW_ALL;
3593
3594   FadeLevelSoundsAndMusic();
3595
3596   ExpireSoundLoops(TRUE);
3597
3598   FadeOut(fade_mask);
3599
3600   if (level_editor_test_game)
3601     FadeSkipNextFadeIn();
3602
3603   // needed if different viewport properties defined for playing
3604   ChangeViewportPropertiesIfNeeded();
3605
3606   ClearField();
3607
3608   DrawCompleteVideoDisplay();
3609
3610   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3611
3612   InitGameEngine();
3613   InitGameControlValues();
3614
3615   if (tape.recording)
3616   {
3617     // initialize tape actions from game when recording tape
3618     tape.use_key_actions   = game.use_key_actions;
3619     tape.use_mouse_actions = game.use_mouse_actions;
3620
3621     // initialize visible playfield size when recording tape (for team mode)
3622     tape.scr_fieldx = SCR_FIELDX;
3623     tape.scr_fieldy = SCR_FIELDY;
3624   }
3625
3626   // don't play tapes over network
3627   network_playing = (network.enabled && !tape.playing);
3628
3629   for (i = 0; i < MAX_PLAYERS; i++)
3630   {
3631     struct PlayerInfo *player = &stored_player[i];
3632
3633     player->index_nr = i;
3634     player->index_bit = (1 << i);
3635     player->element_nr = EL_PLAYER_1 + i;
3636
3637     player->present = FALSE;
3638     player->active = FALSE;
3639     player->mapped = FALSE;
3640
3641     player->killed = FALSE;
3642     player->reanimated = FALSE;
3643     player->buried = FALSE;
3644
3645     player->action = 0;
3646     player->effective_action = 0;
3647     player->programmed_action = 0;
3648     player->snap_action = 0;
3649
3650     player->mouse_action.lx = 0;
3651     player->mouse_action.ly = 0;
3652     player->mouse_action.button = 0;
3653     player->mouse_action.button_hint = 0;
3654
3655     player->effective_mouse_action.lx = 0;
3656     player->effective_mouse_action.ly = 0;
3657     player->effective_mouse_action.button = 0;
3658     player->effective_mouse_action.button_hint = 0;
3659
3660     for (j = 0; j < MAX_NUM_KEYS; j++)
3661       player->key[j] = FALSE;
3662
3663     player->num_white_keys = 0;
3664
3665     player->dynabomb_count = 0;
3666     player->dynabomb_size = 1;
3667     player->dynabombs_left = 0;
3668     player->dynabomb_xl = FALSE;
3669
3670     player->MovDir = initial_move_dir;
3671     player->MovPos = 0;
3672     player->GfxPos = 0;
3673     player->GfxDir = initial_move_dir;
3674     player->GfxAction = ACTION_DEFAULT;
3675     player->Frame = 0;
3676     player->StepFrame = 0;
3677
3678     player->initial_element = player->element_nr;
3679     player->artwork_element =
3680       (level.use_artwork_element[i] ? level.artwork_element[i] :
3681        player->element_nr);
3682     player->use_murphy = FALSE;
3683
3684     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3685     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3686
3687     player->gravity = level.initial_player_gravity[i];
3688
3689     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3690
3691     player->actual_frame_counter.count = 0;
3692     player->actual_frame_counter.value = 1;
3693
3694     player->step_counter = 0;
3695
3696     player->last_move_dir = initial_move_dir;
3697
3698     player->is_active = FALSE;
3699
3700     player->is_waiting = FALSE;
3701     player->is_moving = FALSE;
3702     player->is_auto_moving = FALSE;
3703     player->is_digging = FALSE;
3704     player->is_snapping = FALSE;
3705     player->is_collecting = FALSE;
3706     player->is_pushing = FALSE;
3707     player->is_switching = FALSE;
3708     player->is_dropping = FALSE;
3709     player->is_dropping_pressed = FALSE;
3710
3711     player->is_bored = FALSE;
3712     player->is_sleeping = FALSE;
3713
3714     player->was_waiting = TRUE;
3715     player->was_moving = FALSE;
3716     player->was_snapping = FALSE;
3717     player->was_dropping = FALSE;
3718
3719     player->force_dropping = FALSE;
3720
3721     player->frame_counter_bored = -1;
3722     player->frame_counter_sleeping = -1;
3723
3724     player->anim_delay_counter = 0;
3725     player->post_delay_counter = 0;
3726
3727     player->dir_waiting = initial_move_dir;
3728     player->action_waiting = ACTION_DEFAULT;
3729     player->last_action_waiting = ACTION_DEFAULT;
3730     player->special_action_bored = ACTION_DEFAULT;
3731     player->special_action_sleeping = ACTION_DEFAULT;
3732
3733     player->switch_x = -1;
3734     player->switch_y = -1;
3735
3736     player->drop_x = -1;
3737     player->drop_y = -1;
3738
3739     player->show_envelope = 0;
3740
3741     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3742
3743     player->push_delay       = -1;      // initialized when pushing starts
3744     player->push_delay_value = game.initial_push_delay_value;
3745
3746     player->drop_delay = 0;
3747     player->drop_pressed_delay = 0;
3748
3749     player->last_jx = -1;
3750     player->last_jy = -1;
3751     player->jx = -1;
3752     player->jy = -1;
3753
3754     player->shield_normal_time_left = 0;
3755     player->shield_deadly_time_left = 0;
3756
3757     player->last_removed_element = EL_UNDEFINED;
3758
3759     player->inventory_infinite_element = EL_UNDEFINED;
3760     player->inventory_size = 0;
3761
3762     if (level.use_initial_inventory[i])
3763     {
3764       for (j = 0; j < level.initial_inventory_size[i]; j++)
3765       {
3766         int element = level.initial_inventory_content[i][j];
3767         int collect_count = element_info[element].collect_count_initial;
3768         int k;
3769
3770         if (!IS_CUSTOM_ELEMENT(element))
3771           collect_count = 1;
3772
3773         if (collect_count == 0)
3774           player->inventory_infinite_element = element;
3775         else
3776           for (k = 0; k < collect_count; k++)
3777             if (player->inventory_size < MAX_INVENTORY_SIZE)
3778               player->inventory_element[player->inventory_size++] = element;
3779       }
3780     }
3781
3782     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3783     SnapField(player, 0, 0);
3784
3785     map_player_action[i] = i;
3786   }
3787
3788   network_player_action_received = FALSE;
3789
3790   // initial null action
3791   if (network_playing)
3792     SendToServer_MovePlayer(MV_NONE);
3793
3794   FrameCounter = 0;
3795   TimeFrames = 0;
3796   TimePlayed = 0;
3797   TimeLeft = level.time;
3798   TapeTime = 0;
3799
3800   ScreenMovDir = MV_NONE;
3801   ScreenMovPos = 0;
3802   ScreenGfxPos = 0;
3803
3804   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3805
3806   game.robot_wheel_x = -1;
3807   game.robot_wheel_y = -1;
3808
3809   game.exit_x = -1;
3810   game.exit_y = -1;
3811
3812   game.all_players_gone = FALSE;
3813
3814   game.LevelSolved = FALSE;
3815   game.GameOver = FALSE;
3816
3817   game.GamePlayed = !tape.playing;
3818
3819   game.LevelSolved_GameWon = FALSE;
3820   game.LevelSolved_GameEnd = FALSE;
3821   game.LevelSolved_SaveTape = FALSE;
3822   game.LevelSolved_SaveScore = FALSE;
3823
3824   game.LevelSolved_CountingTime = 0;
3825   game.LevelSolved_CountingScore = 0;
3826   game.LevelSolved_CountingHealth = 0;
3827
3828   game.panel.active = TRUE;
3829
3830   game.no_level_time_limit = (level.time == 0);
3831   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3832
3833   game.yamyam_content_nr = 0;
3834   game.robot_wheel_active = FALSE;
3835   game.magic_wall_active = FALSE;
3836   game.magic_wall_time_left = 0;
3837   game.light_time_left = 0;
3838   game.timegate_time_left = 0;
3839   game.switchgate_pos = 0;
3840   game.wind_direction = level.wind_direction_initial;
3841
3842   game.time_final = 0;
3843   game.score_time_final = 0;
3844
3845   game.score = 0;
3846   game.score_final = 0;
3847
3848   game.health = MAX_HEALTH;
3849   game.health_final = MAX_HEALTH;
3850
3851   game.gems_still_needed = level.gems_needed;
3852   game.sokoban_fields_still_needed = 0;
3853   game.sokoban_objects_still_needed = 0;
3854   game.lights_still_needed = 0;
3855   game.players_still_needed = 0;
3856   game.friends_still_needed = 0;
3857
3858   game.lenses_time_left = 0;
3859   game.magnify_time_left = 0;
3860
3861   game.ball_active = level.ball_active_initial;
3862   game.ball_content_nr = 0;
3863
3864   game.explosions_delayed = TRUE;
3865
3866   game.envelope_active = FALSE;
3867
3868   // special case: set custom artwork setting to initial value
3869   game.use_masked_elements = game.use_masked_elements_initial;
3870
3871   for (i = 0; i < NUM_BELTS; i++)
3872   {
3873     game.belt_dir[i] = MV_NONE;
3874     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3875   }
3876
3877   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3878     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3879
3880 #if DEBUG_INIT_PLAYER
3881   DebugPrintPlayerStatus("Player status at level initialization");
3882 #endif
3883
3884   SCAN_PLAYFIELD(x, y)
3885   {
3886     Tile[x][y] = Last[x][y] = level.field[x][y];
3887     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3888     ChangeDelay[x][y] = 0;
3889     ChangePage[x][y] = -1;
3890     CustomValue[x][y] = 0;              // initialized in InitField()
3891     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3892     AmoebaNr[x][y] = 0;
3893     WasJustMoving[x][y] = 0;
3894     WasJustFalling[x][y] = 0;
3895     CheckCollision[x][y] = 0;
3896     CheckImpact[x][y] = 0;
3897     Stop[x][y] = FALSE;
3898     Pushed[x][y] = FALSE;
3899
3900     ChangeCount[x][y] = 0;
3901     ChangeEvent[x][y] = -1;
3902
3903     ExplodePhase[x][y] = 0;
3904     ExplodeDelay[x][y] = 0;
3905     ExplodeField[x][y] = EX_TYPE_NONE;
3906
3907     RunnerVisit[x][y] = 0;
3908     PlayerVisit[x][y] = 0;
3909
3910     GfxFrame[x][y] = 0;
3911     GfxRandom[x][y] = INIT_GFX_RANDOM();
3912     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3913     GfxElement[x][y] = EL_UNDEFINED;
3914     GfxElementEmpty[x][y] = EL_EMPTY;
3915     GfxAction[x][y] = ACTION_DEFAULT;
3916     GfxDir[x][y] = MV_NONE;
3917     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3918   }
3919
3920   SCAN_PLAYFIELD(x, y)
3921   {
3922     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3923       emulate_bd = FALSE;
3924     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3925       emulate_sp = FALSE;
3926
3927     InitField(x, y, TRUE);
3928
3929     ResetGfxAnimation(x, y);
3930   }
3931
3932   InitBeltMovement();
3933
3934   for (i = 0; i < MAX_PLAYERS; i++)
3935   {
3936     struct PlayerInfo *player = &stored_player[i];
3937
3938     // set number of special actions for bored and sleeping animation
3939     player->num_special_action_bored =
3940       get_num_special_action(player->artwork_element,
3941                              ACTION_BORING_1, ACTION_BORING_LAST);
3942     player->num_special_action_sleeping =
3943       get_num_special_action(player->artwork_element,
3944                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3945   }
3946
3947   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3948                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3949
3950   // initialize type of slippery elements
3951   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3952   {
3953     if (!IS_CUSTOM_ELEMENT(i))
3954     {
3955       // default: elements slip down either to the left or right randomly
3956       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3957
3958       // SP style elements prefer to slip down on the left side
3959       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3960         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3961
3962       // BD style elements prefer to slip down on the left side
3963       if (game.emulation == EMU_BOULDERDASH)
3964         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3965     }
3966   }
3967
3968   // initialize explosion and ignition delay
3969   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3970   {
3971     if (!IS_CUSTOM_ELEMENT(i))
3972     {
3973       int num_phase = 8;
3974       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3975                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3976                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3977       int last_phase = (num_phase + 1) * delay;
3978       int half_phase = (num_phase / 2) * delay;
3979
3980       element_info[i].explosion_delay = last_phase - 1;
3981       element_info[i].ignition_delay = half_phase;
3982
3983       if (i == EL_BLACK_ORB)
3984         element_info[i].ignition_delay = 1;
3985     }
3986   }
3987
3988   // correct non-moving belts to start moving left
3989   for (i = 0; i < NUM_BELTS; i++)
3990     if (game.belt_dir[i] == MV_NONE)
3991       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3992
3993 #if USE_NEW_PLAYER_ASSIGNMENTS
3994   // use preferred player also in local single-player mode
3995   if (!network.enabled && !game.team_mode)
3996   {
3997     int new_index_nr = setup.network_player_nr;
3998
3999     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4000     {
4001       for (i = 0; i < MAX_PLAYERS; i++)
4002         stored_player[i].connected_locally = FALSE;
4003
4004       stored_player[new_index_nr].connected_locally = TRUE;
4005     }
4006   }
4007
4008   for (i = 0; i < MAX_PLAYERS; i++)
4009   {
4010     stored_player[i].connected = FALSE;
4011
4012     // in network game mode, the local player might not be the first player
4013     if (stored_player[i].connected_locally)
4014       local_player = &stored_player[i];
4015   }
4016
4017   if (!network.enabled)
4018     local_player->connected = TRUE;
4019
4020   if (tape.playing)
4021   {
4022     for (i = 0; i < MAX_PLAYERS; i++)
4023       stored_player[i].connected = tape.player_participates[i];
4024   }
4025   else if (network.enabled)
4026   {
4027     // add team mode players connected over the network (needed for correct
4028     // assignment of player figures from level to locally playing players)
4029
4030     for (i = 0; i < MAX_PLAYERS; i++)
4031       if (stored_player[i].connected_network)
4032         stored_player[i].connected = TRUE;
4033   }
4034   else if (game.team_mode)
4035   {
4036     // try to guess locally connected team mode players (needed for correct
4037     // assignment of player figures from level to locally playing players)
4038
4039     for (i = 0; i < MAX_PLAYERS; i++)
4040       if (setup.input[i].use_joystick ||
4041           setup.input[i].key.left != KSYM_UNDEFINED)
4042         stored_player[i].connected = TRUE;
4043   }
4044
4045 #if DEBUG_INIT_PLAYER
4046   DebugPrintPlayerStatus("Player status after level initialization");
4047 #endif
4048
4049 #if DEBUG_INIT_PLAYER
4050   Debug("game:init:player", "Reassigning players ...");
4051 #endif
4052
4053   // check if any connected player was not found in playfield
4054   for (i = 0; i < MAX_PLAYERS; i++)
4055   {
4056     struct PlayerInfo *player = &stored_player[i];
4057
4058     if (player->connected && !player->present)
4059     {
4060       struct PlayerInfo *field_player = NULL;
4061
4062 #if DEBUG_INIT_PLAYER
4063       Debug("game:init:player",
4064             "- looking for field player for player %d ...", i + 1);
4065 #endif
4066
4067       // assign first free player found that is present in the playfield
4068
4069       // first try: look for unmapped playfield player that is not connected
4070       for (j = 0; j < MAX_PLAYERS; j++)
4071         if (field_player == NULL &&
4072             stored_player[j].present &&
4073             !stored_player[j].mapped &&
4074             !stored_player[j].connected)
4075           field_player = &stored_player[j];
4076
4077       // second try: look for *any* unmapped playfield player
4078       for (j = 0; j < MAX_PLAYERS; j++)
4079         if (field_player == NULL &&
4080             stored_player[j].present &&
4081             !stored_player[j].mapped)
4082           field_player = &stored_player[j];
4083
4084       if (field_player != NULL)
4085       {
4086         int jx = field_player->jx, jy = field_player->jy;
4087
4088 #if DEBUG_INIT_PLAYER
4089         Debug("game:init:player", "- found player %d",
4090               field_player->index_nr + 1);
4091 #endif
4092
4093         player->present = FALSE;
4094         player->active = FALSE;
4095
4096         field_player->present = TRUE;
4097         field_player->active = TRUE;
4098
4099         /*
4100         player->initial_element = field_player->initial_element;
4101         player->artwork_element = field_player->artwork_element;
4102
4103         player->block_last_field       = field_player->block_last_field;
4104         player->block_delay_adjustment = field_player->block_delay_adjustment;
4105         */
4106
4107         StorePlayer[jx][jy] = field_player->element_nr;
4108
4109         field_player->jx = field_player->last_jx = jx;
4110         field_player->jy = field_player->last_jy = jy;
4111
4112         if (local_player == player)
4113           local_player = field_player;
4114
4115         map_player_action[field_player->index_nr] = i;
4116
4117         field_player->mapped = TRUE;
4118
4119 #if DEBUG_INIT_PLAYER
4120         Debug("game:init:player", "- map_player_action[%d] == %d",
4121               field_player->index_nr + 1, i + 1);
4122 #endif
4123       }
4124     }
4125
4126     if (player->connected && player->present)
4127       player->mapped = TRUE;
4128   }
4129
4130 #if DEBUG_INIT_PLAYER
4131   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4132 #endif
4133
4134 #else
4135
4136   // check if any connected player was not found in playfield
4137   for (i = 0; i < MAX_PLAYERS; i++)
4138   {
4139     struct PlayerInfo *player = &stored_player[i];
4140
4141     if (player->connected && !player->present)
4142     {
4143       for (j = 0; j < MAX_PLAYERS; j++)
4144       {
4145         struct PlayerInfo *field_player = &stored_player[j];
4146         int jx = field_player->jx, jy = field_player->jy;
4147
4148         // assign first free player found that is present in the playfield
4149         if (field_player->present && !field_player->connected)
4150         {
4151           player->present = TRUE;
4152           player->active = TRUE;
4153
4154           field_player->present = FALSE;
4155           field_player->active = FALSE;
4156
4157           player->initial_element = field_player->initial_element;
4158           player->artwork_element = field_player->artwork_element;
4159
4160           player->block_last_field       = field_player->block_last_field;
4161           player->block_delay_adjustment = field_player->block_delay_adjustment;
4162
4163           StorePlayer[jx][jy] = player->element_nr;
4164
4165           player->jx = player->last_jx = jx;
4166           player->jy = player->last_jy = jy;
4167
4168           break;
4169         }
4170       }
4171     }
4172   }
4173 #endif
4174
4175 #if 0
4176   Debug("game:init:player", "local_player->present == %d",
4177         local_player->present);
4178 #endif
4179
4180   // set focus to local player for network games, else to all players
4181   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4182   game.centered_player_nr_next = game.centered_player_nr;
4183   game.set_centered_player = FALSE;
4184   game.set_centered_player_wrap = FALSE;
4185
4186   if (network_playing && tape.recording)
4187   {
4188     // store client dependent player focus when recording network games
4189     tape.centered_player_nr_next = game.centered_player_nr_next;
4190     tape.set_centered_player = TRUE;
4191   }
4192
4193   if (tape.playing)
4194   {
4195     // when playing a tape, eliminate all players who do not participate
4196
4197 #if USE_NEW_PLAYER_ASSIGNMENTS
4198
4199     if (!game.team_mode)
4200     {
4201       for (i = 0; i < MAX_PLAYERS; i++)
4202       {
4203         if (stored_player[i].active &&
4204             !tape.player_participates[map_player_action[i]])
4205         {
4206           struct PlayerInfo *player = &stored_player[i];
4207           int jx = player->jx, jy = player->jy;
4208
4209 #if DEBUG_INIT_PLAYER
4210           Debug("game:init:player", "Removing player %d at (%d, %d)",
4211                 i + 1, jx, jy);
4212 #endif
4213
4214           player->active = FALSE;
4215           StorePlayer[jx][jy] = 0;
4216           Tile[jx][jy] = EL_EMPTY;
4217         }
4218       }
4219     }
4220
4221 #else
4222
4223     for (i = 0; i < MAX_PLAYERS; i++)
4224     {
4225       if (stored_player[i].active &&
4226           !tape.player_participates[i])
4227       {
4228         struct PlayerInfo *player = &stored_player[i];
4229         int jx = player->jx, jy = player->jy;
4230
4231         player->active = FALSE;
4232         StorePlayer[jx][jy] = 0;
4233         Tile[jx][jy] = EL_EMPTY;
4234       }
4235     }
4236 #endif
4237   }
4238   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4239   {
4240     // when in single player mode, eliminate all but the local player
4241
4242     for (i = 0; i < MAX_PLAYERS; i++)
4243     {
4244       struct PlayerInfo *player = &stored_player[i];
4245
4246       if (player->active && player != local_player)
4247       {
4248         int jx = player->jx, jy = player->jy;
4249
4250         player->active = FALSE;
4251         player->present = FALSE;
4252
4253         StorePlayer[jx][jy] = 0;
4254         Tile[jx][jy] = EL_EMPTY;
4255       }
4256     }
4257   }
4258
4259   for (i = 0; i < MAX_PLAYERS; i++)
4260     if (stored_player[i].active)
4261       game.players_still_needed++;
4262
4263   if (level.solved_by_one_player)
4264     game.players_still_needed = 1;
4265
4266   // when recording the game, store which players take part in the game
4267   if (tape.recording)
4268   {
4269 #if USE_NEW_PLAYER_ASSIGNMENTS
4270     for (i = 0; i < MAX_PLAYERS; i++)
4271       if (stored_player[i].connected)
4272         tape.player_participates[i] = TRUE;
4273 #else
4274     for (i = 0; i < MAX_PLAYERS; i++)
4275       if (stored_player[i].active)
4276         tape.player_participates[i] = TRUE;
4277 #endif
4278   }
4279
4280 #if DEBUG_INIT_PLAYER
4281   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4282 #endif
4283
4284   if (BorderElement == EL_EMPTY)
4285   {
4286     SBX_Left = 0;
4287     SBX_Right = lev_fieldx - SCR_FIELDX;
4288     SBY_Upper = 0;
4289     SBY_Lower = lev_fieldy - SCR_FIELDY;
4290   }
4291   else
4292   {
4293     SBX_Left = -1;
4294     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4295     SBY_Upper = -1;
4296     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4297   }
4298
4299   if (full_lev_fieldx <= SCR_FIELDX)
4300     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4301   if (full_lev_fieldy <= SCR_FIELDY)
4302     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4303
4304   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4305     SBX_Left--;
4306   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4307     SBY_Upper--;
4308
4309   // if local player not found, look for custom element that might create
4310   // the player (make some assumptions about the right custom element)
4311   if (!local_player->present)
4312   {
4313     int start_x = 0, start_y = 0;
4314     int found_rating = 0;
4315     int found_element = EL_UNDEFINED;
4316     int player_nr = local_player->index_nr;
4317
4318     SCAN_PLAYFIELD(x, y)
4319     {
4320       int element = Tile[x][y];
4321       int content;
4322       int xx, yy;
4323       boolean is_player;
4324
4325       if (level.use_start_element[player_nr] &&
4326           level.start_element[player_nr] == element &&
4327           found_rating < 4)
4328       {
4329         start_x = x;
4330         start_y = y;
4331
4332         found_rating = 4;
4333         found_element = element;
4334       }
4335
4336       if (!IS_CUSTOM_ELEMENT(element))
4337         continue;
4338
4339       if (CAN_CHANGE(element))
4340       {
4341         for (i = 0; i < element_info[element].num_change_pages; i++)
4342         {
4343           // check for player created from custom element as single target
4344           content = element_info[element].change_page[i].target_element;
4345           is_player = IS_PLAYER_ELEMENT(content);
4346
4347           if (is_player && (found_rating < 3 ||
4348                             (found_rating == 3 && element < found_element)))
4349           {
4350             start_x = x;
4351             start_y = y;
4352
4353             found_rating = 3;
4354             found_element = element;
4355           }
4356         }
4357       }
4358
4359       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4360       {
4361         // check for player created from custom element as explosion content
4362         content = element_info[element].content.e[xx][yy];
4363         is_player = IS_PLAYER_ELEMENT(content);
4364
4365         if (is_player && (found_rating < 2 ||
4366                           (found_rating == 2 && element < found_element)))
4367         {
4368           start_x = x + xx - 1;
4369           start_y = y + yy - 1;
4370
4371           found_rating = 2;
4372           found_element = element;
4373         }
4374
4375         if (!CAN_CHANGE(element))
4376           continue;
4377
4378         for (i = 0; i < element_info[element].num_change_pages; i++)
4379         {
4380           // check for player created from custom element as extended target
4381           content =
4382             element_info[element].change_page[i].target_content.e[xx][yy];
4383
4384           is_player = IS_PLAYER_ELEMENT(content);
4385
4386           if (is_player && (found_rating < 1 ||
4387                             (found_rating == 1 && element < found_element)))
4388           {
4389             start_x = x + xx - 1;
4390             start_y = y + yy - 1;
4391
4392             found_rating = 1;
4393             found_element = element;
4394           }
4395         }
4396       }
4397     }
4398
4399     scroll_x = SCROLL_POSITION_X(start_x);
4400     scroll_y = SCROLL_POSITION_Y(start_y);
4401   }
4402   else
4403   {
4404     scroll_x = SCROLL_POSITION_X(local_player->jx);
4405     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4406   }
4407
4408   // !!! FIX THIS (START) !!!
4409   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4410   {
4411     InitGameEngine_EM();
4412   }
4413   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4414   {
4415     InitGameEngine_SP();
4416   }
4417   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4418   {
4419     InitGameEngine_MM();
4420   }
4421   else
4422   {
4423     DrawLevel(REDRAW_FIELD);
4424     DrawAllPlayers();
4425
4426     // after drawing the level, correct some elements
4427     if (game.timegate_time_left == 0)
4428       CloseAllOpenTimegates();
4429   }
4430
4431   // blit playfield from scroll buffer to normal back buffer for fading in
4432   BlitScreenToBitmap(backbuffer);
4433   // !!! FIX THIS (END) !!!
4434
4435   DrawMaskedBorder(fade_mask);
4436
4437   FadeIn(fade_mask);
4438
4439 #if 1
4440   // full screen redraw is required at this point in the following cases:
4441   // - special editor door undrawn when game was started from level editor
4442   // - drawing area (playfield) was changed and has to be removed completely
4443   redraw_mask = REDRAW_ALL;
4444   BackToFront();
4445 #endif
4446
4447   if (!game.restart_level)
4448   {
4449     // copy default game door content to main double buffer
4450
4451     // !!! CHECK AGAIN !!!
4452     SetPanelBackground();
4453     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4454     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4455   }
4456
4457   SetPanelBackground();
4458   SetDrawBackgroundMask(REDRAW_DOOR_1);
4459
4460   UpdateAndDisplayGameControlValues();
4461
4462   if (!game.restart_level)
4463   {
4464     UnmapGameButtons();
4465     UnmapTapeButtons();
4466
4467     FreeGameButtons();
4468     CreateGameButtons();
4469
4470     MapGameButtons();
4471     MapTapeButtons();
4472
4473     // copy actual game door content to door double buffer for OpenDoor()
4474     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4475
4476     OpenDoor(DOOR_OPEN_ALL);
4477
4478     KeyboardAutoRepeatOffUnlessAutoplay();
4479
4480 #if DEBUG_INIT_PLAYER
4481     DebugPrintPlayerStatus("Player status (final)");
4482 #endif
4483   }
4484
4485   UnmapAllGadgets();
4486
4487   MapGameButtons();
4488   MapTapeButtons();
4489
4490   if (!game.restart_level && !tape.playing)
4491   {
4492     LevelStats_incPlayed(level_nr);
4493
4494     SaveLevelSetup_SeriesInfo();
4495   }
4496
4497   game.restart_level = FALSE;
4498   game.restart_game_message = NULL;
4499
4500   game.request_active = FALSE;
4501   game.request_active_or_moving = FALSE;
4502
4503   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4504     InitGameActions_MM();
4505
4506   SaveEngineSnapshotToListInitial();
4507
4508   if (!game.restart_level)
4509   {
4510     PlaySound(SND_GAME_STARTING);
4511
4512     if (setup.sound_music)
4513       PlayLevelMusic();
4514   }
4515
4516   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4517 }
4518
4519 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4520                         int actual_player_x, int actual_player_y)
4521 {
4522   // this is used for non-R'n'D game engines to update certain engine values
4523
4524   // needed to determine if sounds are played within the visible screen area
4525   scroll_x = actual_scroll_x;
4526   scroll_y = actual_scroll_y;
4527
4528   // needed to get player position for "follow finger" playing input method
4529   local_player->jx = actual_player_x;
4530   local_player->jy = actual_player_y;
4531 }
4532
4533 void InitMovDir(int x, int y)
4534 {
4535   int i, element = Tile[x][y];
4536   static int xy[4][2] =
4537   {
4538     {  0, +1 },
4539     { +1,  0 },
4540     {  0, -1 },
4541     { -1,  0 }
4542   };
4543   static int direction[3][4] =
4544   {
4545     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4546     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4547     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4548   };
4549
4550   switch (element)
4551   {
4552     case EL_BUG_RIGHT:
4553     case EL_BUG_UP:
4554     case EL_BUG_LEFT:
4555     case EL_BUG_DOWN:
4556       Tile[x][y] = EL_BUG;
4557       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4558       break;
4559
4560     case EL_SPACESHIP_RIGHT:
4561     case EL_SPACESHIP_UP:
4562     case EL_SPACESHIP_LEFT:
4563     case EL_SPACESHIP_DOWN:
4564       Tile[x][y] = EL_SPACESHIP;
4565       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4566       break;
4567
4568     case EL_BD_BUTTERFLY_RIGHT:
4569     case EL_BD_BUTTERFLY_UP:
4570     case EL_BD_BUTTERFLY_LEFT:
4571     case EL_BD_BUTTERFLY_DOWN:
4572       Tile[x][y] = EL_BD_BUTTERFLY;
4573       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4574       break;
4575
4576     case EL_BD_FIREFLY_RIGHT:
4577     case EL_BD_FIREFLY_UP:
4578     case EL_BD_FIREFLY_LEFT:
4579     case EL_BD_FIREFLY_DOWN:
4580       Tile[x][y] = EL_BD_FIREFLY;
4581       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4582       break;
4583
4584     case EL_PACMAN_RIGHT:
4585     case EL_PACMAN_UP:
4586     case EL_PACMAN_LEFT:
4587     case EL_PACMAN_DOWN:
4588       Tile[x][y] = EL_PACMAN;
4589       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4590       break;
4591
4592     case EL_YAMYAM_LEFT:
4593     case EL_YAMYAM_RIGHT:
4594     case EL_YAMYAM_UP:
4595     case EL_YAMYAM_DOWN:
4596       Tile[x][y] = EL_YAMYAM;
4597       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4598       break;
4599
4600     case EL_SP_SNIKSNAK:
4601       MovDir[x][y] = MV_UP;
4602       break;
4603
4604     case EL_SP_ELECTRON:
4605       MovDir[x][y] = MV_LEFT;
4606       break;
4607
4608     case EL_MOLE_LEFT:
4609     case EL_MOLE_RIGHT:
4610     case EL_MOLE_UP:
4611     case EL_MOLE_DOWN:
4612       Tile[x][y] = EL_MOLE;
4613       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4614       break;
4615
4616     case EL_SPRING_LEFT:
4617     case EL_SPRING_RIGHT:
4618       Tile[x][y] = EL_SPRING;
4619       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4620       break;
4621
4622     default:
4623       if (IS_CUSTOM_ELEMENT(element))
4624       {
4625         struct ElementInfo *ei = &element_info[element];
4626         int move_direction_initial = ei->move_direction_initial;
4627         int move_pattern = ei->move_pattern;
4628
4629         if (move_direction_initial == MV_START_PREVIOUS)
4630         {
4631           if (MovDir[x][y] != MV_NONE)
4632             return;
4633
4634           move_direction_initial = MV_START_AUTOMATIC;
4635         }
4636
4637         if (move_direction_initial == MV_START_RANDOM)
4638           MovDir[x][y] = 1 << RND(4);
4639         else if (move_direction_initial & MV_ANY_DIRECTION)
4640           MovDir[x][y] = move_direction_initial;
4641         else if (move_pattern == MV_ALL_DIRECTIONS ||
4642                  move_pattern == MV_TURNING_LEFT ||
4643                  move_pattern == MV_TURNING_RIGHT ||
4644                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4645                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4646                  move_pattern == MV_TURNING_RANDOM)
4647           MovDir[x][y] = 1 << RND(4);
4648         else if (move_pattern == MV_HORIZONTAL)
4649           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4650         else if (move_pattern == MV_VERTICAL)
4651           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4652         else if (move_pattern & MV_ANY_DIRECTION)
4653           MovDir[x][y] = element_info[element].move_pattern;
4654         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4655                  move_pattern == MV_ALONG_RIGHT_SIDE)
4656         {
4657           // use random direction as default start direction
4658           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4659             MovDir[x][y] = 1 << RND(4);
4660
4661           for (i = 0; i < NUM_DIRECTIONS; i++)
4662           {
4663             int x1 = x + xy[i][0];
4664             int y1 = y + xy[i][1];
4665
4666             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4667             {
4668               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4669                 MovDir[x][y] = direction[0][i];
4670               else
4671                 MovDir[x][y] = direction[1][i];
4672
4673               break;
4674             }
4675           }
4676         }                
4677       }
4678       else
4679       {
4680         MovDir[x][y] = 1 << RND(4);
4681
4682         if (element != EL_BUG &&
4683             element != EL_SPACESHIP &&
4684             element != EL_BD_BUTTERFLY &&
4685             element != EL_BD_FIREFLY)
4686           break;
4687
4688         for (i = 0; i < NUM_DIRECTIONS; i++)
4689         {
4690           int x1 = x + xy[i][0];
4691           int y1 = y + xy[i][1];
4692
4693           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4694           {
4695             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4696             {
4697               MovDir[x][y] = direction[0][i];
4698               break;
4699             }
4700             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4701                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4702             {
4703               MovDir[x][y] = direction[1][i];
4704               break;
4705             }
4706           }
4707         }
4708       }
4709       break;
4710   }
4711
4712   GfxDir[x][y] = MovDir[x][y];
4713 }
4714
4715 void InitAmoebaNr(int x, int y)
4716 {
4717   int i;
4718   int group_nr = AmoebaNeighbourNr(x, y);
4719
4720   if (group_nr == 0)
4721   {
4722     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4723     {
4724       if (AmoebaCnt[i] == 0)
4725       {
4726         group_nr = i;
4727         break;
4728       }
4729     }
4730   }
4731
4732   AmoebaNr[x][y] = group_nr;
4733   AmoebaCnt[group_nr]++;
4734   AmoebaCnt2[group_nr]++;
4735 }
4736
4737 static void LevelSolved_SetFinalGameValues(void)
4738 {
4739   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4740   game.score_time_final = (level.use_step_counter ? TimePlayed :
4741                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4742
4743   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4744                       game_em.lev->score :
4745                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4746                       game_mm.score :
4747                       game.score);
4748
4749   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4750                        MM_HEALTH(game_mm.laser_overload_value) :
4751                        game.health);
4752
4753   game.LevelSolved_CountingTime = game.time_final;
4754   game.LevelSolved_CountingScore = game.score_final;
4755   game.LevelSolved_CountingHealth = game.health_final;
4756 }
4757
4758 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4759 {
4760   game.LevelSolved_CountingTime = time;
4761   game.LevelSolved_CountingScore = score;
4762   game.LevelSolved_CountingHealth = health;
4763
4764   game_panel_controls[GAME_PANEL_TIME].value = time;
4765   game_panel_controls[GAME_PANEL_SCORE].value = score;
4766   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4767
4768   DisplayGameControlValues();
4769 }
4770
4771 static void LevelSolved(void)
4772 {
4773   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4774       game.players_still_needed > 0)
4775     return;
4776
4777   game.LevelSolved = TRUE;
4778   game.GameOver = TRUE;
4779
4780   // needed here to display correct panel values while player walks into exit
4781   LevelSolved_SetFinalGameValues();
4782 }
4783
4784 void GameWon(void)
4785 {
4786   static int time_count_steps;
4787   static int time, time_final;
4788   static float score, score_final; // needed for time score < 10 for 10 seconds
4789   static int health, health_final;
4790   static int game_over_delay_1 = 0;
4791   static int game_over_delay_2 = 0;
4792   static int game_over_delay_3 = 0;
4793   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4794   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4795
4796   if (!game.LevelSolved_GameWon)
4797   {
4798     int i;
4799
4800     // do not start end game actions before the player stops moving (to exit)
4801     if (local_player->active && local_player->MovPos)
4802       return;
4803
4804     // calculate final game values after player finished walking into exit
4805     LevelSolved_SetFinalGameValues();
4806
4807     game.LevelSolved_GameWon = TRUE;
4808     game.LevelSolved_SaveTape = tape.recording;
4809     game.LevelSolved_SaveScore = !tape.playing;
4810
4811     if (!tape.playing)
4812     {
4813       LevelStats_incSolved(level_nr);
4814
4815       SaveLevelSetup_SeriesInfo();
4816     }
4817
4818     if (tape.auto_play)         // tape might already be stopped here
4819       tape.auto_play_level_solved = TRUE;
4820
4821     TapeStop();
4822
4823     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4824     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4825     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4826
4827     time = time_final = game.time_final;
4828     score = score_final = game.score_final;
4829     health = health_final = game.health_final;
4830
4831     // update game panel values before (delayed) counting of score (if any)
4832     LevelSolved_DisplayFinalGameValues(time, score, health);
4833
4834     // if level has time score defined, calculate new final game values
4835     if (time_score > 0)
4836     {
4837       int time_final_max = 999;
4838       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4839       int time_frames = 0;
4840       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4841       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4842
4843       if (TimeLeft > 0)
4844       {
4845         time_final = 0;
4846         time_frames = time_frames_left;
4847       }
4848       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4849       {
4850         time_final = time_final_max;
4851         time_frames = time_frames_final_max - time_frames_played;
4852       }
4853
4854       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4855
4856       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4857
4858       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4859       {
4860         health_final = 0;
4861         score_final += health * time_score;
4862       }
4863
4864       game.score_final = score_final;
4865       game.health_final = health_final;
4866     }
4867
4868     // if not counting score after game, immediately update game panel values
4869     if (level_editor_test_game || !setup.count_score_after_game)
4870     {
4871       time = time_final;
4872       score = score_final;
4873
4874       LevelSolved_DisplayFinalGameValues(time, score, health);
4875     }
4876
4877     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4878     {
4879       // check if last player has left the level
4880       if (game.exit_x >= 0 &&
4881           game.exit_y >= 0)
4882       {
4883         int x = game.exit_x;
4884         int y = game.exit_y;
4885         int element = Tile[x][y];
4886
4887         // close exit door after last player
4888         if ((game.all_players_gone &&
4889              (element == EL_EXIT_OPEN ||
4890               element == EL_SP_EXIT_OPEN ||
4891               element == EL_STEEL_EXIT_OPEN)) ||
4892             element == EL_EM_EXIT_OPEN ||
4893             element == EL_EM_STEEL_EXIT_OPEN)
4894         {
4895
4896           Tile[x][y] =
4897             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4898              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4899              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4900              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4901              EL_EM_STEEL_EXIT_CLOSING);
4902
4903           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4904         }
4905
4906         // player disappears
4907         DrawLevelField(x, y);
4908       }
4909
4910       for (i = 0; i < MAX_PLAYERS; i++)
4911       {
4912         struct PlayerInfo *player = &stored_player[i];
4913
4914         if (player->present)
4915         {
4916           RemovePlayer(player);
4917
4918           // player disappears
4919           DrawLevelField(player->jx, player->jy);
4920         }
4921       }
4922     }
4923
4924     PlaySound(SND_GAME_WINNING);
4925   }
4926
4927   if (setup.count_score_after_game)
4928   {
4929     if (time != time_final)
4930     {
4931       if (game_over_delay_1 > 0)
4932       {
4933         game_over_delay_1--;
4934
4935         return;
4936       }
4937
4938       int time_to_go = ABS(time_final - time);
4939       int time_count_dir = (time < time_final ? +1 : -1);
4940
4941       if (time_to_go < time_count_steps)
4942         time_count_steps = 1;
4943
4944       time  += time_count_steps * time_count_dir;
4945       score += time_count_steps * time_score;
4946
4947       // set final score to correct rounding differences after counting score
4948       if (time == time_final)
4949         score = score_final;
4950
4951       LevelSolved_DisplayFinalGameValues(time, score, health);
4952
4953       if (time == time_final)
4954         StopSound(SND_GAME_LEVELTIME_BONUS);
4955       else if (setup.sound_loops)
4956         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4957       else
4958         PlaySound(SND_GAME_LEVELTIME_BONUS);
4959
4960       return;
4961     }
4962
4963     if (health != health_final)
4964     {
4965       if (game_over_delay_2 > 0)
4966       {
4967         game_over_delay_2--;
4968
4969         return;
4970       }
4971
4972       int health_count_dir = (health < health_final ? +1 : -1);
4973
4974       health += health_count_dir;
4975       score  += time_score;
4976
4977       LevelSolved_DisplayFinalGameValues(time, score, health);
4978
4979       if (health == health_final)
4980         StopSound(SND_GAME_LEVELTIME_BONUS);
4981       else if (setup.sound_loops)
4982         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4983       else
4984         PlaySound(SND_GAME_LEVELTIME_BONUS);
4985
4986       return;
4987     }
4988   }
4989
4990   game.panel.active = FALSE;
4991
4992   if (game_over_delay_3 > 0)
4993   {
4994     game_over_delay_3--;
4995
4996     return;
4997   }
4998
4999   GameEnd();
5000 }
5001
5002 void GameEnd(void)
5003 {
5004   // used instead of "level_nr" (needed for network games)
5005   int last_level_nr = levelset.level_nr;
5006   boolean tape_saved = FALSE;
5007
5008   game.LevelSolved_GameEnd = TRUE;
5009
5010   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5011   {
5012     // make sure that request dialog to save tape does not open door again
5013     if (!global.use_envelope_request)
5014       CloseDoor(DOOR_CLOSE_1);
5015
5016     // ask to save tape
5017     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5018
5019     // set unique basename for score tape (also saved in high score table)
5020     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5021   }
5022
5023   // if no tape is to be saved, close both doors simultaneously
5024   CloseDoor(DOOR_CLOSE_ALL);
5025
5026   if (level_editor_test_game || score_info_tape_play)
5027   {
5028     SetGameStatus(GAME_MODE_MAIN);
5029
5030     DrawMainMenu();
5031
5032     return;
5033   }
5034
5035   if (!game.LevelSolved_SaveScore)
5036   {
5037     SetGameStatus(GAME_MODE_MAIN);
5038
5039     DrawMainMenu();
5040
5041     return;
5042   }
5043
5044   if (level_nr == leveldir_current->handicap_level)
5045   {
5046     leveldir_current->handicap_level++;
5047
5048     SaveLevelSetup_SeriesInfo();
5049   }
5050
5051   // save score and score tape before potentially erasing tape below
5052   NewHighScore(last_level_nr, tape_saved);
5053
5054   if (setup.increment_levels &&
5055       level_nr < leveldir_current->last_level &&
5056       !network_playing)
5057   {
5058     level_nr++;         // advance to next level
5059     TapeErase();        // start with empty tape
5060
5061     if (setup.auto_play_next_level)
5062     {
5063       scores.continue_playing = TRUE;
5064       scores.next_level_nr = level_nr;
5065
5066       LoadLevel(level_nr);
5067
5068       SaveLevelSetup_SeriesInfo();
5069     }
5070   }
5071
5072   if (scores.last_added >= 0 && setup.show_scores_after_game)
5073   {
5074     SetGameStatus(GAME_MODE_SCORES);
5075
5076     DrawHallOfFame(last_level_nr);
5077   }
5078   else if (scores.continue_playing)
5079   {
5080     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5081   }
5082   else
5083   {
5084     SetGameStatus(GAME_MODE_MAIN);
5085
5086     DrawMainMenu();
5087   }
5088 }
5089
5090 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5091                          boolean one_score_entry_per_name)
5092 {
5093   int i;
5094
5095   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5096     return -1;
5097
5098   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5099   {
5100     struct ScoreEntry *entry = &list->entry[i];
5101     boolean score_is_better = (new_entry->score >  entry->score);
5102     boolean score_is_equal  = (new_entry->score == entry->score);
5103     boolean time_is_better  = (new_entry->time  <  entry->time);
5104     boolean time_is_equal   = (new_entry->time  == entry->time);
5105     boolean better_by_score = (score_is_better ||
5106                                (score_is_equal && time_is_better));
5107     boolean better_by_time  = (time_is_better ||
5108                                (time_is_equal && score_is_better));
5109     boolean is_better = (level.rate_time_over_score ? better_by_time :
5110                          better_by_score);
5111     boolean entry_is_empty = (entry->score == 0 &&
5112                               entry->time == 0);
5113
5114     // prevent adding server score entries if also existing in local score file
5115     // (special case: historic score entries have an empty tape basename entry)
5116     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5117         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5118     {
5119       // add fields from server score entry not stored in local score entry
5120       // (currently, this means setting platform, version and country fields;
5121       // in rare cases, this may also correct an invalid score value, as
5122       // historic scores might have been truncated to 16-bit values locally)
5123       *entry = *new_entry;
5124
5125       return -1;
5126     }
5127
5128     if (is_better || entry_is_empty)
5129     {
5130       // player has made it to the hall of fame
5131
5132       if (i < MAX_SCORE_ENTRIES - 1)
5133       {
5134         int m = MAX_SCORE_ENTRIES - 1;
5135         int l;
5136
5137         if (one_score_entry_per_name)
5138         {
5139           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5140             if (strEqual(list->entry[l].name, new_entry->name))
5141               m = l;
5142
5143           if (m == i)   // player's new highscore overwrites his old one
5144             goto put_into_list;
5145         }
5146
5147         for (l = m; l > i; l--)
5148           list->entry[l] = list->entry[l - 1];
5149       }
5150
5151       put_into_list:
5152
5153       *entry = *new_entry;
5154
5155       return i;
5156     }
5157     else if (one_score_entry_per_name &&
5158              strEqual(entry->name, new_entry->name))
5159     {
5160       // player already in high score list with better score or time
5161
5162       return -1;
5163     }
5164   }
5165
5166   // special case: new score is beyond the last high score list position
5167   return MAX_SCORE_ENTRIES;
5168 }
5169
5170 void NewHighScore(int level_nr, boolean tape_saved)
5171 {
5172   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5173   boolean one_per_name = FALSE;
5174
5175   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5176   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5177
5178   new_entry.score = game.score_final;
5179   new_entry.time = game.score_time_final;
5180
5181   LoadScore(level_nr);
5182
5183   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5184
5185   if (scores.last_added >= MAX_SCORE_ENTRIES)
5186   {
5187     scores.last_added = MAX_SCORE_ENTRIES - 1;
5188     scores.force_last_added = TRUE;
5189
5190     scores.entry[scores.last_added] = new_entry;
5191
5192     // store last added local score entry (before merging server scores)
5193     scores.last_added_local = scores.last_added;
5194
5195     return;
5196   }
5197
5198   if (scores.last_added < 0)
5199     return;
5200
5201   SaveScore(level_nr);
5202
5203   // store last added local score entry (before merging server scores)
5204   scores.last_added_local = scores.last_added;
5205
5206   if (!game.LevelSolved_SaveTape)
5207     return;
5208
5209   SaveScoreTape(level_nr);
5210
5211   if (setup.ask_for_using_api_server)
5212   {
5213     setup.use_api_server =
5214       Request("Upload your score and tape to the high score server?", REQ_ASK);
5215
5216     if (!setup.use_api_server)
5217       Request("Not using high score server! Use setup menu to enable again!",
5218               REQ_CONFIRM);
5219
5220     runtime.use_api_server = setup.use_api_server;
5221
5222     // after asking for using API server once, do not ask again
5223     setup.ask_for_using_api_server = FALSE;
5224
5225     SaveSetup_ServerSetup();
5226   }
5227
5228   SaveServerScore(level_nr, tape_saved);
5229 }
5230
5231 void MergeServerScore(void)
5232 {
5233   struct ScoreEntry last_added_entry;
5234   boolean one_per_name = FALSE;
5235   int i;
5236
5237   if (scores.last_added >= 0)
5238     last_added_entry = scores.entry[scores.last_added];
5239
5240   for (i = 0; i < server_scores.num_entries; i++)
5241   {
5242     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5243
5244     if (pos >= 0 && pos <= scores.last_added)
5245       scores.last_added++;
5246   }
5247
5248   if (scores.last_added >= MAX_SCORE_ENTRIES)
5249   {
5250     scores.last_added = MAX_SCORE_ENTRIES - 1;
5251     scores.force_last_added = TRUE;
5252
5253     scores.entry[scores.last_added] = last_added_entry;
5254   }
5255 }
5256
5257 static int getElementMoveStepsizeExt(int x, int y, int direction)
5258 {
5259   int element = Tile[x][y];
5260   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5261   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5262   int horiz_move = (dx != 0);
5263   int sign = (horiz_move ? dx : dy);
5264   int step = sign * element_info[element].move_stepsize;
5265
5266   // special values for move stepsize for spring and things on conveyor belt
5267   if (horiz_move)
5268   {
5269     if (CAN_FALL(element) &&
5270         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5271       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5272     else if (element == EL_SPRING)
5273       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5274   }
5275
5276   return step;
5277 }
5278
5279 static int getElementMoveStepsize(int x, int y)
5280 {
5281   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5282 }
5283
5284 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5285 {
5286   if (player->GfxAction != action || player->GfxDir != dir)
5287   {
5288     player->GfxAction = action;
5289     player->GfxDir = dir;
5290     player->Frame = 0;
5291     player->StepFrame = 0;
5292   }
5293 }
5294
5295 static void ResetGfxFrame(int x, int y)
5296 {
5297   // profiling showed that "autotest" spends 10~20% of its time in this function
5298   if (DrawingDeactivatedField())
5299     return;
5300
5301   int element = Tile[x][y];
5302   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5303
5304   if (graphic_info[graphic].anim_global_sync)
5305     GfxFrame[x][y] = FrameCounter;
5306   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5307     GfxFrame[x][y] = CustomValue[x][y];
5308   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5309     GfxFrame[x][y] = element_info[element].collect_score;
5310   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5311     GfxFrame[x][y] = ChangeDelay[x][y];
5312 }
5313
5314 static void ResetGfxAnimation(int x, int y)
5315 {
5316   GfxAction[x][y] = ACTION_DEFAULT;
5317   GfxDir[x][y] = MovDir[x][y];
5318   GfxFrame[x][y] = 0;
5319
5320   ResetGfxFrame(x, y);
5321 }
5322
5323 static void ResetRandomAnimationValue(int x, int y)
5324 {
5325   GfxRandom[x][y] = INIT_GFX_RANDOM();
5326 }
5327
5328 static void InitMovingField(int x, int y, int direction)
5329 {
5330   int element = Tile[x][y];
5331   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5332   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5333   int newx = x + dx;
5334   int newy = y + dy;
5335   boolean is_moving_before, is_moving_after;
5336
5337   // check if element was/is moving or being moved before/after mode change
5338   is_moving_before = (WasJustMoving[x][y] != 0);
5339   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5340
5341   // reset animation only for moving elements which change direction of moving
5342   // or which just started or stopped moving
5343   // (else CEs with property "can move" / "not moving" are reset each frame)
5344   if (is_moving_before != is_moving_after ||
5345       direction != MovDir[x][y])
5346     ResetGfxAnimation(x, y);
5347
5348   MovDir[x][y] = direction;
5349   GfxDir[x][y] = direction;
5350
5351   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5352                      direction == MV_DOWN && CAN_FALL(element) ?
5353                      ACTION_FALLING : ACTION_MOVING);
5354
5355   // this is needed for CEs with property "can move" / "not moving"
5356
5357   if (is_moving_after)
5358   {
5359     if (Tile[newx][newy] == EL_EMPTY)
5360       Tile[newx][newy] = EL_BLOCKED;
5361
5362     MovDir[newx][newy] = MovDir[x][y];
5363
5364     CustomValue[newx][newy] = CustomValue[x][y];
5365
5366     GfxFrame[newx][newy] = GfxFrame[x][y];
5367     GfxRandom[newx][newy] = GfxRandom[x][y];
5368     GfxAction[newx][newy] = GfxAction[x][y];
5369     GfxDir[newx][newy] = GfxDir[x][y];
5370   }
5371 }
5372
5373 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5374 {
5375   int direction = MovDir[x][y];
5376   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5377   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5378
5379   *goes_to_x = newx;
5380   *goes_to_y = newy;
5381 }
5382
5383 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5384 {
5385   int oldx = x, oldy = y;
5386   int direction = MovDir[x][y];
5387
5388   if (direction == MV_LEFT)
5389     oldx++;
5390   else if (direction == MV_RIGHT)
5391     oldx--;
5392   else if (direction == MV_UP)
5393     oldy++;
5394   else if (direction == MV_DOWN)
5395     oldy--;
5396
5397   *comes_from_x = oldx;
5398   *comes_from_y = oldy;
5399 }
5400
5401 static int MovingOrBlocked2Element(int x, int y)
5402 {
5403   int element = Tile[x][y];
5404
5405   if (element == EL_BLOCKED)
5406   {
5407     int oldx, oldy;
5408
5409     Blocked2Moving(x, y, &oldx, &oldy);
5410     return Tile[oldx][oldy];
5411   }
5412   else
5413     return element;
5414 }
5415
5416 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5417 {
5418   // like MovingOrBlocked2Element(), but if element is moving
5419   // and (x,y) is the field the moving element is just leaving,
5420   // return EL_BLOCKED instead of the element value
5421   int element = Tile[x][y];
5422
5423   if (IS_MOVING(x, y))
5424   {
5425     if (element == EL_BLOCKED)
5426     {
5427       int oldx, oldy;
5428
5429       Blocked2Moving(x, y, &oldx, &oldy);
5430       return Tile[oldx][oldy];
5431     }
5432     else
5433       return EL_BLOCKED;
5434   }
5435   else
5436     return element;
5437 }
5438
5439 static void RemoveField(int x, int y)
5440 {
5441   Tile[x][y] = EL_EMPTY;
5442
5443   MovPos[x][y] = 0;
5444   MovDir[x][y] = 0;
5445   MovDelay[x][y] = 0;
5446
5447   CustomValue[x][y] = 0;
5448
5449   AmoebaNr[x][y] = 0;
5450   ChangeDelay[x][y] = 0;
5451   ChangePage[x][y] = -1;
5452   Pushed[x][y] = FALSE;
5453
5454   GfxElement[x][y] = EL_UNDEFINED;
5455   GfxAction[x][y] = ACTION_DEFAULT;
5456   GfxDir[x][y] = MV_NONE;
5457 }
5458
5459 static void RemoveMovingField(int x, int y)
5460 {
5461   int oldx = x, oldy = y, newx = x, newy = y;
5462   int element = Tile[x][y];
5463   int next_element = EL_UNDEFINED;
5464
5465   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5466     return;
5467
5468   if (IS_MOVING(x, y))
5469   {
5470     Moving2Blocked(x, y, &newx, &newy);
5471
5472     if (Tile[newx][newy] != EL_BLOCKED)
5473     {
5474       // element is moving, but target field is not free (blocked), but
5475       // already occupied by something different (example: acid pool);
5476       // in this case, only remove the moving field, but not the target
5477
5478       RemoveField(oldx, oldy);
5479
5480       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5481
5482       TEST_DrawLevelField(oldx, oldy);
5483
5484       return;
5485     }
5486   }
5487   else if (element == EL_BLOCKED)
5488   {
5489     Blocked2Moving(x, y, &oldx, &oldy);
5490     if (!IS_MOVING(oldx, oldy))
5491       return;
5492   }
5493
5494   if (element == EL_BLOCKED &&
5495       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5496        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5497        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5498        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5499        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5500        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5501     next_element = get_next_element(Tile[oldx][oldy]);
5502
5503   RemoveField(oldx, oldy);
5504   RemoveField(newx, newy);
5505
5506   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5507
5508   if (next_element != EL_UNDEFINED)
5509     Tile[oldx][oldy] = next_element;
5510
5511   TEST_DrawLevelField(oldx, oldy);
5512   TEST_DrawLevelField(newx, newy);
5513 }
5514
5515 void DrawDynamite(int x, int y)
5516 {
5517   int sx = SCREENX(x), sy = SCREENY(y);
5518   int graphic = el2img(Tile[x][y]);
5519   int frame;
5520
5521   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5522     return;
5523
5524   if (IS_WALKABLE_INSIDE(Back[x][y]))
5525     return;
5526
5527   if (Back[x][y])
5528     DrawLevelElement(x, y, Back[x][y]);
5529   else if (Store[x][y])
5530     DrawLevelElement(x, y, Store[x][y]);
5531   else if (game.use_masked_elements)
5532     DrawLevelElement(x, y, EL_EMPTY);
5533
5534   frame = getGraphicAnimationFrameXY(graphic, x, y);
5535
5536   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5537     DrawGraphicThruMask(sx, sy, graphic, frame);
5538   else
5539     DrawGraphic(sx, sy, graphic, frame);
5540 }
5541
5542 static void CheckDynamite(int x, int y)
5543 {
5544   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5545   {
5546     MovDelay[x][y]--;
5547
5548     if (MovDelay[x][y] != 0)
5549     {
5550       DrawDynamite(x, y);
5551       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5552
5553       return;
5554     }
5555   }
5556
5557   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5558
5559   Bang(x, y);
5560 }
5561
5562 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5563 {
5564   boolean num_checked_players = 0;
5565   int i;
5566
5567   for (i = 0; i < MAX_PLAYERS; i++)
5568   {
5569     if (stored_player[i].active)
5570     {
5571       int sx = stored_player[i].jx;
5572       int sy = stored_player[i].jy;
5573
5574       if (num_checked_players == 0)
5575       {
5576         *sx1 = *sx2 = sx;
5577         *sy1 = *sy2 = sy;
5578       }
5579       else
5580       {
5581         *sx1 = MIN(*sx1, sx);
5582         *sy1 = MIN(*sy1, sy);
5583         *sx2 = MAX(*sx2, sx);
5584         *sy2 = MAX(*sy2, sy);
5585       }
5586
5587       num_checked_players++;
5588     }
5589   }
5590 }
5591
5592 static boolean checkIfAllPlayersFitToScreen_RND(void)
5593 {
5594   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5595
5596   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5597
5598   return (sx2 - sx1 < SCR_FIELDX &&
5599           sy2 - sy1 < SCR_FIELDY);
5600 }
5601
5602 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5603 {
5604   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5605
5606   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5607
5608   *sx = (sx1 + sx2) / 2;
5609   *sy = (sy1 + sy2) / 2;
5610 }
5611
5612 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5613                                boolean center_screen, boolean quick_relocation)
5614 {
5615   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5616   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5617   boolean no_delay = (tape.warp_forward);
5618   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5619   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5620   int new_scroll_x, new_scroll_y;
5621
5622   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5623   {
5624     // case 1: quick relocation inside visible screen (without scrolling)
5625
5626     RedrawPlayfield();
5627
5628     return;
5629   }
5630
5631   if (!level.shifted_relocation || center_screen)
5632   {
5633     // relocation _with_ centering of screen
5634
5635     new_scroll_x = SCROLL_POSITION_X(x);
5636     new_scroll_y = SCROLL_POSITION_Y(y);
5637   }
5638   else
5639   {
5640     // relocation _without_ centering of screen
5641
5642     int center_scroll_x = SCROLL_POSITION_X(old_x);
5643     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5644     int offset_x = x + (scroll_x - center_scroll_x);
5645     int offset_y = y + (scroll_y - center_scroll_y);
5646
5647     // for new screen position, apply previous offset to center position
5648     new_scroll_x = SCROLL_POSITION_X(offset_x);
5649     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5650   }
5651
5652   if (quick_relocation)
5653   {
5654     // case 2: quick relocation (redraw without visible scrolling)
5655
5656     scroll_x = new_scroll_x;
5657     scroll_y = new_scroll_y;
5658
5659     RedrawPlayfield();
5660
5661     return;
5662   }
5663
5664   // case 3: visible relocation (with scrolling to new position)
5665
5666   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5667
5668   SetVideoFrameDelay(wait_delay_value);
5669
5670   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5671   {
5672     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5673     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5674
5675     if (dx == 0 && dy == 0)             // no scrolling needed at all
5676       break;
5677
5678     scroll_x -= dx;
5679     scroll_y -= dy;
5680
5681     // set values for horizontal/vertical screen scrolling (half tile size)
5682     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5683     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5684     int pos_x = dx * TILEX / 2;
5685     int pos_y = dy * TILEY / 2;
5686     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5687     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5688
5689     ScrollLevel(dx, dy);
5690     DrawAllPlayers();
5691
5692     // scroll in two steps of half tile size to make things smoother
5693     BlitScreenToBitmapExt_RND(window, fx, fy);
5694
5695     // scroll second step to align at full tile size
5696     BlitScreenToBitmap(window);
5697   }
5698
5699   DrawAllPlayers();
5700   BackToFront();
5701
5702   SetVideoFrameDelay(frame_delay_value_old);
5703 }
5704
5705 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5706 {
5707   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5708   int player_nr = GET_PLAYER_NR(el_player);
5709   struct PlayerInfo *player = &stored_player[player_nr];
5710   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5711   boolean no_delay = (tape.warp_forward);
5712   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5713   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5714   int old_jx = player->jx;
5715   int old_jy = player->jy;
5716   int old_element = Tile[old_jx][old_jy];
5717   int element = Tile[jx][jy];
5718   boolean player_relocated = (old_jx != jx || old_jy != jy);
5719
5720   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5721   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5722   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5723   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5724   int leave_side_horiz = move_dir_horiz;
5725   int leave_side_vert  = move_dir_vert;
5726   int enter_side = enter_side_horiz | enter_side_vert;
5727   int leave_side = leave_side_horiz | leave_side_vert;
5728
5729   if (player->buried)           // do not reanimate dead player
5730     return;
5731
5732   if (!player_relocated)        // no need to relocate the player
5733     return;
5734
5735   if (IS_PLAYER(jx, jy))        // player already placed at new position
5736   {
5737     RemoveField(jx, jy);        // temporarily remove newly placed player
5738     DrawLevelField(jx, jy);
5739   }
5740
5741   if (player->present)
5742   {
5743     while (player->MovPos)
5744     {
5745       ScrollPlayer(player, SCROLL_GO_ON);
5746       ScrollScreen(NULL, SCROLL_GO_ON);
5747
5748       AdvanceFrameAndPlayerCounters(player->index_nr);
5749
5750       DrawPlayer(player);
5751
5752       BackToFront_WithFrameDelay(wait_delay_value);
5753     }
5754
5755     DrawPlayer(player);         // needed here only to cleanup last field
5756     DrawLevelField(player->jx, player->jy);     // remove player graphic
5757
5758     player->is_moving = FALSE;
5759   }
5760
5761   if (IS_CUSTOM_ELEMENT(old_element))
5762     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5763                                CE_LEFT_BY_PLAYER,
5764                                player->index_bit, leave_side);
5765
5766   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5767                                       CE_PLAYER_LEAVES_X,
5768                                       player->index_bit, leave_side);
5769
5770   Tile[jx][jy] = el_player;
5771   InitPlayerField(jx, jy, el_player, TRUE);
5772
5773   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5774      possible that the relocation target field did not contain a player element,
5775      but a walkable element, to which the new player was relocated -- in this
5776      case, restore that (already initialized!) element on the player field */
5777   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5778   {
5779     Tile[jx][jy] = element;     // restore previously existing element
5780   }
5781
5782   // only visually relocate centered player
5783   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5784                      FALSE, level.instant_relocation);
5785
5786   TestIfPlayerTouchesBadThing(jx, jy);
5787   TestIfPlayerTouchesCustomElement(jx, jy);
5788
5789   if (IS_CUSTOM_ELEMENT(element))
5790     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5791                                player->index_bit, enter_side);
5792
5793   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5794                                       player->index_bit, enter_side);
5795
5796   if (player->is_switching)
5797   {
5798     /* ensure that relocation while still switching an element does not cause
5799        a new element to be treated as also switched directly after relocation
5800        (this is important for teleporter switches that teleport the player to
5801        a place where another teleporter switch is in the same direction, which
5802        would then incorrectly be treated as immediately switched before the
5803        direction key that caused the switch was released) */
5804
5805     player->switch_x += jx - old_jx;
5806     player->switch_y += jy - old_jy;
5807   }
5808 }
5809
5810 static void Explode(int ex, int ey, int phase, int mode)
5811 {
5812   int x, y;
5813   int last_phase;
5814   int border_element;
5815
5816   // !!! eliminate this variable !!!
5817   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5818
5819   if (game.explosions_delayed)
5820   {
5821     ExplodeField[ex][ey] = mode;
5822     return;
5823   }
5824
5825   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5826   {
5827     int center_element = Tile[ex][ey];
5828     int artwork_element, explosion_element;     // set these values later
5829
5830     // remove things displayed in background while burning dynamite
5831     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5832       Back[ex][ey] = 0;
5833
5834     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5835     {
5836       // put moving element to center field (and let it explode there)
5837       center_element = MovingOrBlocked2Element(ex, ey);
5838       RemoveMovingField(ex, ey);
5839       Tile[ex][ey] = center_element;
5840     }
5841
5842     // now "center_element" is finally determined -- set related values now
5843     artwork_element = center_element;           // for custom player artwork
5844     explosion_element = center_element;         // for custom player artwork
5845
5846     if (IS_PLAYER(ex, ey))
5847     {
5848       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5849
5850       artwork_element = stored_player[player_nr].artwork_element;
5851
5852       if (level.use_explosion_element[player_nr])
5853       {
5854         explosion_element = level.explosion_element[player_nr];
5855         artwork_element = explosion_element;
5856       }
5857     }
5858
5859     if (mode == EX_TYPE_NORMAL ||
5860         mode == EX_TYPE_CENTER ||
5861         mode == EX_TYPE_CROSS)
5862       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5863
5864     last_phase = element_info[explosion_element].explosion_delay + 1;
5865
5866     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5867     {
5868       int xx = x - ex + 1;
5869       int yy = y - ey + 1;
5870       int element;
5871
5872       if (!IN_LEV_FIELD(x, y) ||
5873           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5874           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5875         continue;
5876
5877       element = Tile[x][y];
5878
5879       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5880       {
5881         element = MovingOrBlocked2Element(x, y);
5882
5883         if (!IS_EXPLOSION_PROOF(element))
5884           RemoveMovingField(x, y);
5885       }
5886
5887       // indestructible elements can only explode in center (but not flames)
5888       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5889                                            mode == EX_TYPE_BORDER)) ||
5890           element == EL_FLAMES)
5891         continue;
5892
5893       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5894          behaviour, for example when touching a yamyam that explodes to rocks
5895          with active deadly shield, a rock is created under the player !!! */
5896       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5897 #if 0
5898       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5899           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5900            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5901 #else
5902       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5903 #endif
5904       {
5905         if (IS_ACTIVE_BOMB(element))
5906         {
5907           // re-activate things under the bomb like gate or penguin
5908           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5909           Back[x][y] = 0;
5910         }
5911
5912         continue;
5913       }
5914
5915       // save walkable background elements while explosion on same tile
5916       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5917           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5918         Back[x][y] = element;
5919
5920       // ignite explodable elements reached by other explosion
5921       if (element == EL_EXPLOSION)
5922         element = Store2[x][y];
5923
5924       if (AmoebaNr[x][y] &&
5925           (element == EL_AMOEBA_FULL ||
5926            element == EL_BD_AMOEBA ||
5927            element == EL_AMOEBA_GROWING))
5928       {
5929         AmoebaCnt[AmoebaNr[x][y]]--;
5930         AmoebaCnt2[AmoebaNr[x][y]]--;
5931       }
5932
5933       RemoveField(x, y);
5934
5935       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5936       {
5937         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5938
5939         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5940
5941         if (PLAYERINFO(ex, ey)->use_murphy)
5942           Store[x][y] = EL_EMPTY;
5943       }
5944
5945       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5946       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5947       else if (IS_PLAYER_ELEMENT(center_element))
5948         Store[x][y] = EL_EMPTY;
5949       else if (center_element == EL_YAMYAM)
5950         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5951       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5952         Store[x][y] = element_info[center_element].content.e[xx][yy];
5953 #if 1
5954       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5955       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5956       // otherwise) -- FIX THIS !!!
5957       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5958         Store[x][y] = element_info[element].content.e[1][1];
5959 #else
5960       else if (!CAN_EXPLODE(element))
5961         Store[x][y] = element_info[element].content.e[1][1];
5962 #endif
5963       else
5964         Store[x][y] = EL_EMPTY;
5965
5966       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5967           center_element == EL_AMOEBA_TO_DIAMOND)
5968         Store2[x][y] = element;
5969
5970       Tile[x][y] = EL_EXPLOSION;
5971       GfxElement[x][y] = artwork_element;
5972
5973       ExplodePhase[x][y] = 1;
5974       ExplodeDelay[x][y] = last_phase;
5975
5976       Stop[x][y] = TRUE;
5977     }
5978
5979     if (center_element == EL_YAMYAM)
5980       game.yamyam_content_nr =
5981         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5982
5983     return;
5984   }
5985
5986   if (Stop[ex][ey])
5987     return;
5988
5989   x = ex;
5990   y = ey;
5991
5992   if (phase == 1)
5993     GfxFrame[x][y] = 0;         // restart explosion animation
5994
5995   last_phase = ExplodeDelay[x][y];
5996
5997   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5998
5999   // this can happen if the player leaves an explosion just in time
6000   if (GfxElement[x][y] == EL_UNDEFINED)
6001     GfxElement[x][y] = EL_EMPTY;
6002
6003   border_element = Store2[x][y];
6004   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6005     border_element = StorePlayer[x][y];
6006
6007   if (phase == element_info[border_element].ignition_delay ||
6008       phase == last_phase)
6009   {
6010     boolean border_explosion = FALSE;
6011
6012     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6013         !PLAYER_EXPLOSION_PROTECTED(x, y))
6014     {
6015       KillPlayerUnlessExplosionProtected(x, y);
6016       border_explosion = TRUE;
6017     }
6018     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6019     {
6020       Tile[x][y] = Store2[x][y];
6021       Store2[x][y] = 0;
6022       Bang(x, y);
6023       border_explosion = TRUE;
6024     }
6025     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6026     {
6027       AmoebaToDiamond(x, y);
6028       Store2[x][y] = 0;
6029       border_explosion = TRUE;
6030     }
6031
6032     // if an element just explodes due to another explosion (chain-reaction),
6033     // do not immediately end the new explosion when it was the last frame of
6034     // the explosion (as it would be done in the following "if"-statement!)
6035     if (border_explosion && phase == last_phase)
6036       return;
6037   }
6038
6039   // this can happen if the player was just killed by an explosion
6040   if (GfxElement[x][y] == EL_UNDEFINED)
6041     GfxElement[x][y] = EL_EMPTY;
6042
6043   if (phase == last_phase)
6044   {
6045     int element;
6046
6047     element = Tile[x][y] = Store[x][y];
6048     Store[x][y] = Store2[x][y] = 0;
6049     GfxElement[x][y] = EL_UNDEFINED;
6050
6051     // player can escape from explosions and might therefore be still alive
6052     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6053         element <= EL_PLAYER_IS_EXPLODING_4)
6054     {
6055       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6056       int explosion_element = EL_PLAYER_1 + player_nr;
6057       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6058       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6059
6060       if (level.use_explosion_element[player_nr])
6061         explosion_element = level.explosion_element[player_nr];
6062
6063       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6064                     element_info[explosion_element].content.e[xx][yy]);
6065     }
6066
6067     // restore probably existing indestructible background element
6068     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6069       element = Tile[x][y] = Back[x][y];
6070     Back[x][y] = 0;
6071
6072     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6073     GfxDir[x][y] = MV_NONE;
6074     ChangeDelay[x][y] = 0;
6075     ChangePage[x][y] = -1;
6076
6077     CustomValue[x][y] = 0;
6078
6079     InitField_WithBug2(x, y, FALSE);
6080
6081     TEST_DrawLevelField(x, y);
6082
6083     TestIfElementTouchesCustomElement(x, y);
6084
6085     if (GFX_CRUMBLED(element))
6086       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6087
6088     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6089       StorePlayer[x][y] = 0;
6090
6091     if (IS_PLAYER_ELEMENT(element))
6092       RelocatePlayer(x, y, element);
6093   }
6094   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6095   {
6096     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6097     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6098
6099     if (phase == delay)
6100       TEST_DrawLevelFieldCrumbled(x, y);
6101
6102     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6103     {
6104       DrawLevelElement(x, y, Back[x][y]);
6105       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6106     }
6107     else if (IS_WALKABLE_UNDER(Back[x][y]))
6108     {
6109       DrawLevelGraphic(x, y, graphic, frame);
6110       DrawLevelElementThruMask(x, y, Back[x][y]);
6111     }
6112     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6113       DrawLevelGraphic(x, y, graphic, frame);
6114   }
6115 }
6116
6117 static void DynaExplode(int ex, int ey)
6118 {
6119   int i, j;
6120   int dynabomb_element = Tile[ex][ey];
6121   int dynabomb_size = 1;
6122   boolean dynabomb_xl = FALSE;
6123   struct PlayerInfo *player;
6124   struct XY *xy = xy_topdown;
6125
6126   if (IS_ACTIVE_BOMB(dynabomb_element))
6127   {
6128     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6129     dynabomb_size = player->dynabomb_size;
6130     dynabomb_xl = player->dynabomb_xl;
6131     player->dynabombs_left++;
6132   }
6133
6134   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6135
6136   for (i = 0; i < NUM_DIRECTIONS; i++)
6137   {
6138     for (j = 1; j <= dynabomb_size; j++)
6139     {
6140       int x = ex + j * xy[i].x;
6141       int y = ey + j * xy[i].y;
6142       int element;
6143
6144       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6145         break;
6146
6147       element = Tile[x][y];
6148
6149       // do not restart explosions of fields with active bombs
6150       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6151         continue;
6152
6153       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6154
6155       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6156           !IS_DIGGABLE(element) && !dynabomb_xl)
6157         break;
6158     }
6159   }
6160 }
6161
6162 void Bang(int x, int y)
6163 {
6164   int element = MovingOrBlocked2Element(x, y);
6165   int explosion_type = EX_TYPE_NORMAL;
6166
6167   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6168   {
6169     struct PlayerInfo *player = PLAYERINFO(x, y);
6170
6171     element = Tile[x][y] = player->initial_element;
6172
6173     if (level.use_explosion_element[player->index_nr])
6174     {
6175       int explosion_element = level.explosion_element[player->index_nr];
6176
6177       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6178         explosion_type = EX_TYPE_CROSS;
6179       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6180         explosion_type = EX_TYPE_CENTER;
6181     }
6182   }
6183
6184   switch (element)
6185   {
6186     case EL_BUG:
6187     case EL_SPACESHIP:
6188     case EL_BD_BUTTERFLY:
6189     case EL_BD_FIREFLY:
6190     case EL_YAMYAM:
6191     case EL_DARK_YAMYAM:
6192     case EL_ROBOT:
6193     case EL_PACMAN:
6194     case EL_MOLE:
6195       RaiseScoreElement(element);
6196       break;
6197
6198     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6199     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6200     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6201     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6202     case EL_DYNABOMB_INCREASE_NUMBER:
6203     case EL_DYNABOMB_INCREASE_SIZE:
6204     case EL_DYNABOMB_INCREASE_POWER:
6205       explosion_type = EX_TYPE_DYNA;
6206       break;
6207
6208     case EL_DC_LANDMINE:
6209       explosion_type = EX_TYPE_CENTER;
6210       break;
6211
6212     case EL_PENGUIN:
6213     case EL_LAMP:
6214     case EL_LAMP_ACTIVE:
6215     case EL_AMOEBA_TO_DIAMOND:
6216       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6217         explosion_type = EX_TYPE_CENTER;
6218       break;
6219
6220     default:
6221       if (element_info[element].explosion_type == EXPLODES_CROSS)
6222         explosion_type = EX_TYPE_CROSS;
6223       else if (element_info[element].explosion_type == EXPLODES_1X1)
6224         explosion_type = EX_TYPE_CENTER;
6225       break;
6226   }
6227
6228   if (explosion_type == EX_TYPE_DYNA)
6229     DynaExplode(x, y);
6230   else
6231     Explode(x, y, EX_PHASE_START, explosion_type);
6232
6233   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6234 }
6235
6236 static void SplashAcid(int x, int y)
6237 {
6238   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6239       (!IN_LEV_FIELD(x - 1, y - 2) ||
6240        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6241     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6242
6243   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6244       (!IN_LEV_FIELD(x + 1, y - 2) ||
6245        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6246     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6247
6248   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6249 }
6250
6251 static void InitBeltMovement(void)
6252 {
6253   static int belt_base_element[4] =
6254   {
6255     EL_CONVEYOR_BELT_1_LEFT,
6256     EL_CONVEYOR_BELT_2_LEFT,
6257     EL_CONVEYOR_BELT_3_LEFT,
6258     EL_CONVEYOR_BELT_4_LEFT
6259   };
6260   static int belt_base_active_element[4] =
6261   {
6262     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6263     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6264     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6265     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6266   };
6267
6268   int x, y, i, j;
6269
6270   // set frame order for belt animation graphic according to belt direction
6271   for (i = 0; i < NUM_BELTS; i++)
6272   {
6273     int belt_nr = i;
6274
6275     for (j = 0; j < NUM_BELT_PARTS; j++)
6276     {
6277       int element = belt_base_active_element[belt_nr] + j;
6278       int graphic_1 = el2img(element);
6279       int graphic_2 = el2panelimg(element);
6280
6281       if (game.belt_dir[i] == MV_LEFT)
6282       {
6283         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6284         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6285       }
6286       else
6287       {
6288         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6289         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6290       }
6291     }
6292   }
6293
6294   SCAN_PLAYFIELD(x, y)
6295   {
6296     int element = Tile[x][y];
6297
6298     for (i = 0; i < NUM_BELTS; i++)
6299     {
6300       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6301       {
6302         int e_belt_nr = getBeltNrFromBeltElement(element);
6303         int belt_nr = i;
6304
6305         if (e_belt_nr == belt_nr)
6306         {
6307           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6308
6309           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6310         }
6311       }
6312     }
6313   }
6314 }
6315
6316 static void ToggleBeltSwitch(int x, int y)
6317 {
6318   static int belt_base_element[4] =
6319   {
6320     EL_CONVEYOR_BELT_1_LEFT,
6321     EL_CONVEYOR_BELT_2_LEFT,
6322     EL_CONVEYOR_BELT_3_LEFT,
6323     EL_CONVEYOR_BELT_4_LEFT
6324   };
6325   static int belt_base_active_element[4] =
6326   {
6327     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6328     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6329     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6330     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6331   };
6332   static int belt_base_switch_element[4] =
6333   {
6334     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6335     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6336     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6337     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6338   };
6339   static int belt_move_dir[4] =
6340   {
6341     MV_LEFT,
6342     MV_NONE,
6343     MV_RIGHT,
6344     MV_NONE,
6345   };
6346
6347   int element = Tile[x][y];
6348   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6349   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6350   int belt_dir = belt_move_dir[belt_dir_nr];
6351   int xx, yy, i;
6352
6353   if (!IS_BELT_SWITCH(element))
6354     return;
6355
6356   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6357   game.belt_dir[belt_nr] = belt_dir;
6358
6359   if (belt_dir_nr == 3)
6360     belt_dir_nr = 1;
6361
6362   // set frame order for belt animation graphic according to belt direction
6363   for (i = 0; i < NUM_BELT_PARTS; i++)
6364   {
6365     int element = belt_base_active_element[belt_nr] + i;
6366     int graphic_1 = el2img(element);
6367     int graphic_2 = el2panelimg(element);
6368
6369     if (belt_dir == MV_LEFT)
6370     {
6371       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6372       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6373     }
6374     else
6375     {
6376       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6377       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6378     }
6379   }
6380
6381   SCAN_PLAYFIELD(xx, yy)
6382   {
6383     int element = Tile[xx][yy];
6384
6385     if (IS_BELT_SWITCH(element))
6386     {
6387       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6388
6389       if (e_belt_nr == belt_nr)
6390       {
6391         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6392         TEST_DrawLevelField(xx, yy);
6393       }
6394     }
6395     else if (IS_BELT(element) && belt_dir != MV_NONE)
6396     {
6397       int e_belt_nr = getBeltNrFromBeltElement(element);
6398
6399       if (e_belt_nr == belt_nr)
6400       {
6401         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6402
6403         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6404         TEST_DrawLevelField(xx, yy);
6405       }
6406     }
6407     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6408     {
6409       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6410
6411       if (e_belt_nr == belt_nr)
6412       {
6413         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6414
6415         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6416         TEST_DrawLevelField(xx, yy);
6417       }
6418     }
6419   }
6420 }
6421
6422 static void ToggleSwitchgateSwitch(int x, int y)
6423 {
6424   int xx, yy;
6425
6426   game.switchgate_pos = !game.switchgate_pos;
6427
6428   SCAN_PLAYFIELD(xx, yy)
6429   {
6430     int element = Tile[xx][yy];
6431
6432     if (element == EL_SWITCHGATE_SWITCH_UP)
6433     {
6434       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6435       TEST_DrawLevelField(xx, yy);
6436     }
6437     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6438     {
6439       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6440       TEST_DrawLevelField(xx, yy);
6441     }
6442     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6443     {
6444       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6445       TEST_DrawLevelField(xx, yy);
6446     }
6447     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6448     {
6449       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6450       TEST_DrawLevelField(xx, yy);
6451     }
6452     else if (element == EL_SWITCHGATE_OPEN ||
6453              element == EL_SWITCHGATE_OPENING)
6454     {
6455       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6456
6457       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6458     }
6459     else if (element == EL_SWITCHGATE_CLOSED ||
6460              element == EL_SWITCHGATE_CLOSING)
6461     {
6462       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6463
6464       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6465     }
6466   }
6467 }
6468
6469 static int getInvisibleActiveFromInvisibleElement(int element)
6470 {
6471   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6472           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6473           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6474           element);
6475 }
6476
6477 static int getInvisibleFromInvisibleActiveElement(int element)
6478 {
6479   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6480           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6481           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6482           element);
6483 }
6484
6485 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6486 {
6487   int x, y;
6488
6489   SCAN_PLAYFIELD(x, y)
6490   {
6491     int element = Tile[x][y];
6492
6493     if (element == EL_LIGHT_SWITCH &&
6494         game.light_time_left > 0)
6495     {
6496       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6497       TEST_DrawLevelField(x, y);
6498     }
6499     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6500              game.light_time_left == 0)
6501     {
6502       Tile[x][y] = EL_LIGHT_SWITCH;
6503       TEST_DrawLevelField(x, y);
6504     }
6505     else if (element == EL_EMC_DRIPPER &&
6506              game.light_time_left > 0)
6507     {
6508       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6509       TEST_DrawLevelField(x, y);
6510     }
6511     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6512              game.light_time_left == 0)
6513     {
6514       Tile[x][y] = EL_EMC_DRIPPER;
6515       TEST_DrawLevelField(x, y);
6516     }
6517     else if (element == EL_INVISIBLE_STEELWALL ||
6518              element == EL_INVISIBLE_WALL ||
6519              element == EL_INVISIBLE_SAND)
6520     {
6521       if (game.light_time_left > 0)
6522         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6523
6524       TEST_DrawLevelField(x, y);
6525
6526       // uncrumble neighbour fields, if needed
6527       if (element == EL_INVISIBLE_SAND)
6528         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6529     }
6530     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6531              element == EL_INVISIBLE_WALL_ACTIVE ||
6532              element == EL_INVISIBLE_SAND_ACTIVE)
6533     {
6534       if (game.light_time_left == 0)
6535         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6536
6537       TEST_DrawLevelField(x, y);
6538
6539       // re-crumble neighbour fields, if needed
6540       if (element == EL_INVISIBLE_SAND)
6541         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6542     }
6543   }
6544 }
6545
6546 static void RedrawAllInvisibleElementsForLenses(void)
6547 {
6548   int x, y;
6549
6550   SCAN_PLAYFIELD(x, y)
6551   {
6552     int element = Tile[x][y];
6553
6554     if (element == EL_EMC_DRIPPER &&
6555         game.lenses_time_left > 0)
6556     {
6557       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6558       TEST_DrawLevelField(x, y);
6559     }
6560     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6561              game.lenses_time_left == 0)
6562     {
6563       Tile[x][y] = EL_EMC_DRIPPER;
6564       TEST_DrawLevelField(x, y);
6565     }
6566     else if (element == EL_INVISIBLE_STEELWALL ||
6567              element == EL_INVISIBLE_WALL ||
6568              element == EL_INVISIBLE_SAND)
6569     {
6570       if (game.lenses_time_left > 0)
6571         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6572
6573       TEST_DrawLevelField(x, y);
6574
6575       // uncrumble neighbour fields, if needed
6576       if (element == EL_INVISIBLE_SAND)
6577         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6578     }
6579     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6580              element == EL_INVISIBLE_WALL_ACTIVE ||
6581              element == EL_INVISIBLE_SAND_ACTIVE)
6582     {
6583       if (game.lenses_time_left == 0)
6584         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6585
6586       TEST_DrawLevelField(x, y);
6587
6588       // re-crumble neighbour fields, if needed
6589       if (element == EL_INVISIBLE_SAND)
6590         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6591     }
6592   }
6593 }
6594
6595 static void RedrawAllInvisibleElementsForMagnifier(void)
6596 {
6597   int x, y;
6598
6599   SCAN_PLAYFIELD(x, y)
6600   {
6601     int element = Tile[x][y];
6602
6603     if (element == EL_EMC_FAKE_GRASS &&
6604         game.magnify_time_left > 0)
6605     {
6606       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6607       TEST_DrawLevelField(x, y);
6608     }
6609     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6610              game.magnify_time_left == 0)
6611     {
6612       Tile[x][y] = EL_EMC_FAKE_GRASS;
6613       TEST_DrawLevelField(x, y);
6614     }
6615     else if (IS_GATE_GRAY(element) &&
6616              game.magnify_time_left > 0)
6617     {
6618       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6619                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6620                     IS_EM_GATE_GRAY(element) ?
6621                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6622                     IS_EMC_GATE_GRAY(element) ?
6623                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6624                     IS_DC_GATE_GRAY(element) ?
6625                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6626                     element);
6627       TEST_DrawLevelField(x, y);
6628     }
6629     else if (IS_GATE_GRAY_ACTIVE(element) &&
6630              game.magnify_time_left == 0)
6631     {
6632       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6633                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6634                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6635                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6636                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6637                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6638                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6639                     EL_DC_GATE_WHITE_GRAY :
6640                     element);
6641       TEST_DrawLevelField(x, y);
6642     }
6643   }
6644 }
6645
6646 static void ToggleLightSwitch(int x, int y)
6647 {
6648   int element = Tile[x][y];
6649
6650   game.light_time_left =
6651     (element == EL_LIGHT_SWITCH ?
6652      level.time_light * FRAMES_PER_SECOND : 0);
6653
6654   RedrawAllLightSwitchesAndInvisibleElements();
6655 }
6656
6657 static void ActivateTimegateSwitch(int x, int y)
6658 {
6659   int xx, yy;
6660
6661   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6662
6663   SCAN_PLAYFIELD(xx, yy)
6664   {
6665     int element = Tile[xx][yy];
6666
6667     if (element == EL_TIMEGATE_CLOSED ||
6668         element == EL_TIMEGATE_CLOSING)
6669     {
6670       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6671       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6672     }
6673
6674     /*
6675     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6676     {
6677       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6678       TEST_DrawLevelField(xx, yy);
6679     }
6680     */
6681
6682   }
6683
6684   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6685                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6686 }
6687
6688 static void Impact(int x, int y)
6689 {
6690   boolean last_line = (y == lev_fieldy - 1);
6691   boolean object_hit = FALSE;
6692   boolean impact = (last_line || object_hit);
6693   int element = Tile[x][y];
6694   int smashed = EL_STEELWALL;
6695
6696   if (!last_line)       // check if element below was hit
6697   {
6698     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6699       return;
6700
6701     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6702                                          MovDir[x][y + 1] != MV_DOWN ||
6703                                          MovPos[x][y + 1] <= TILEY / 2));
6704
6705     // do not smash moving elements that left the smashed field in time
6706     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6707         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6708       object_hit = FALSE;
6709
6710 #if USE_QUICKSAND_IMPACT_BUGFIX
6711     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6712     {
6713       RemoveMovingField(x, y + 1);
6714       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6715       Tile[x][y + 2] = EL_ROCK;
6716       TEST_DrawLevelField(x, y + 2);
6717
6718       object_hit = TRUE;
6719     }
6720
6721     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6722     {
6723       RemoveMovingField(x, y + 1);
6724       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6725       Tile[x][y + 2] = EL_ROCK;
6726       TEST_DrawLevelField(x, y + 2);
6727
6728       object_hit = TRUE;
6729     }
6730 #endif
6731
6732     if (object_hit)
6733       smashed = MovingOrBlocked2Element(x, y + 1);
6734
6735     impact = (last_line || object_hit);
6736   }
6737
6738   if (!last_line && smashed == EL_ACID) // element falls into acid
6739   {
6740     SplashAcid(x, y + 1);
6741     return;
6742   }
6743
6744   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6745   // only reset graphic animation if graphic really changes after impact
6746   if (impact &&
6747       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6748   {
6749     ResetGfxAnimation(x, y);
6750     TEST_DrawLevelField(x, y);
6751   }
6752
6753   if (impact && CAN_EXPLODE_IMPACT(element))
6754   {
6755     Bang(x, y);
6756     return;
6757   }
6758   else if (impact && element == EL_PEARL &&
6759            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6760   {
6761     ResetGfxAnimation(x, y);
6762
6763     Tile[x][y] = EL_PEARL_BREAKING;
6764     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6765     return;
6766   }
6767   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6768   {
6769     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6770
6771     return;
6772   }
6773
6774   if (impact && element == EL_AMOEBA_DROP)
6775   {
6776     if (object_hit && IS_PLAYER(x, y + 1))
6777       KillPlayerUnlessEnemyProtected(x, y + 1);
6778     else if (object_hit && smashed == EL_PENGUIN)
6779       Bang(x, y + 1);
6780     else
6781     {
6782       Tile[x][y] = EL_AMOEBA_GROWING;
6783       Store[x][y] = EL_AMOEBA_WET;
6784
6785       ResetRandomAnimationValue(x, y);
6786     }
6787     return;
6788   }
6789
6790   if (object_hit)               // check which object was hit
6791   {
6792     if ((CAN_PASS_MAGIC_WALL(element) && 
6793          (smashed == EL_MAGIC_WALL ||
6794           smashed == EL_BD_MAGIC_WALL)) ||
6795         (CAN_PASS_DC_MAGIC_WALL(element) &&
6796          smashed == EL_DC_MAGIC_WALL))
6797     {
6798       int xx, yy;
6799       int activated_magic_wall =
6800         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6801          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6802          EL_DC_MAGIC_WALL_ACTIVE);
6803
6804       // activate magic wall / mill
6805       SCAN_PLAYFIELD(xx, yy)
6806       {
6807         if (Tile[xx][yy] == smashed)
6808           Tile[xx][yy] = activated_magic_wall;
6809       }
6810
6811       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6812       game.magic_wall_active = TRUE;
6813
6814       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6815                             SND_MAGIC_WALL_ACTIVATING :
6816                             smashed == EL_BD_MAGIC_WALL ?
6817                             SND_BD_MAGIC_WALL_ACTIVATING :
6818                             SND_DC_MAGIC_WALL_ACTIVATING));
6819     }
6820
6821     if (IS_PLAYER(x, y + 1))
6822     {
6823       if (CAN_SMASH_PLAYER(element))
6824       {
6825         KillPlayerUnlessEnemyProtected(x, y + 1);
6826         return;
6827       }
6828     }
6829     else if (smashed == EL_PENGUIN)
6830     {
6831       if (CAN_SMASH_PLAYER(element))
6832       {
6833         Bang(x, y + 1);
6834         return;
6835       }
6836     }
6837     else if (element == EL_BD_DIAMOND)
6838     {
6839       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6840       {
6841         Bang(x, y + 1);
6842         return;
6843       }
6844     }
6845     else if (((element == EL_SP_INFOTRON ||
6846                element == EL_SP_ZONK) &&
6847               (smashed == EL_SP_SNIKSNAK ||
6848                smashed == EL_SP_ELECTRON ||
6849                smashed == EL_SP_DISK_ORANGE)) ||
6850              (element == EL_SP_INFOTRON &&
6851               smashed == EL_SP_DISK_YELLOW))
6852     {
6853       Bang(x, y + 1);
6854       return;
6855     }
6856     else if (CAN_SMASH_EVERYTHING(element))
6857     {
6858       if (IS_CLASSIC_ENEMY(smashed) ||
6859           CAN_EXPLODE_SMASHED(smashed))
6860       {
6861         Bang(x, y + 1);
6862         return;
6863       }
6864       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6865       {
6866         if (smashed == EL_LAMP ||
6867             smashed == EL_LAMP_ACTIVE)
6868         {
6869           Bang(x, y + 1);
6870           return;
6871         }
6872         else if (smashed == EL_NUT)
6873         {
6874           Tile[x][y + 1] = EL_NUT_BREAKING;
6875           PlayLevelSound(x, y, SND_NUT_BREAKING);
6876           RaiseScoreElement(EL_NUT);
6877           return;
6878         }
6879         else if (smashed == EL_PEARL)
6880         {
6881           ResetGfxAnimation(x, y);
6882
6883           Tile[x][y + 1] = EL_PEARL_BREAKING;
6884           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6885           return;
6886         }
6887         else if (smashed == EL_DIAMOND)
6888         {
6889           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6890           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6891           return;
6892         }
6893         else if (IS_BELT_SWITCH(smashed))
6894         {
6895           ToggleBeltSwitch(x, y + 1);
6896         }
6897         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6898                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6899                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6900                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6901         {
6902           ToggleSwitchgateSwitch(x, y + 1);
6903         }
6904         else if (smashed == EL_LIGHT_SWITCH ||
6905                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6906         {
6907           ToggleLightSwitch(x, y + 1);
6908         }
6909         else
6910         {
6911           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6912
6913           CheckElementChangeBySide(x, y + 1, smashed, element,
6914                                    CE_SWITCHED, CH_SIDE_TOP);
6915           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6916                                             CH_SIDE_TOP);
6917         }
6918       }
6919       else
6920       {
6921         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6922       }
6923     }
6924   }
6925
6926   // play sound of magic wall / mill
6927   if (!last_line &&
6928       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6929        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6930        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6931   {
6932     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6933       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6934     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6935       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6936     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6937       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6938
6939     return;
6940   }
6941
6942   // play sound of object that hits the ground
6943   if (last_line || object_hit)
6944     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6945 }
6946
6947 static void TurnRoundExt(int x, int y)
6948 {
6949   static struct
6950   {
6951     int dx, dy;
6952   } move_xy[] =
6953   {
6954     {  0,  0 },
6955     { -1,  0 },
6956     { +1,  0 },
6957     {  0,  0 },
6958     {  0, -1 },
6959     {  0,  0 }, { 0, 0 }, { 0, 0 },
6960     {  0, +1 }
6961   };
6962   static struct
6963   {
6964     int left, right, back;
6965   } turn[] =
6966   {
6967     { 0,        0,              0        },
6968     { MV_DOWN,  MV_UP,          MV_RIGHT },
6969     { MV_UP,    MV_DOWN,        MV_LEFT  },
6970     { 0,        0,              0        },
6971     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6972     { 0,        0,              0        },
6973     { 0,        0,              0        },
6974     { 0,        0,              0        },
6975     { MV_RIGHT, MV_LEFT,        MV_UP    }
6976   };
6977
6978   int element = Tile[x][y];
6979   int move_pattern = element_info[element].move_pattern;
6980
6981   int old_move_dir = MovDir[x][y];
6982   int left_dir  = turn[old_move_dir].left;
6983   int right_dir = turn[old_move_dir].right;
6984   int back_dir  = turn[old_move_dir].back;
6985
6986   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6987   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6988   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6989   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6990
6991   int left_x  = x + left_dx,  left_y  = y + left_dy;
6992   int right_x = x + right_dx, right_y = y + right_dy;
6993   int move_x  = x + move_dx,  move_y  = y + move_dy;
6994
6995   int xx, yy;
6996
6997   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6998   {
6999     TestIfBadThingTouchesOtherBadThing(x, y);
7000
7001     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7002       MovDir[x][y] = right_dir;
7003     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7004       MovDir[x][y] = left_dir;
7005
7006     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7007       MovDelay[x][y] = 9;
7008     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7009       MovDelay[x][y] = 1;
7010   }
7011   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7012   {
7013     TestIfBadThingTouchesOtherBadThing(x, y);
7014
7015     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7016       MovDir[x][y] = left_dir;
7017     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7018       MovDir[x][y] = right_dir;
7019
7020     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7021       MovDelay[x][y] = 9;
7022     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7023       MovDelay[x][y] = 1;
7024   }
7025   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7026   {
7027     TestIfBadThingTouchesOtherBadThing(x, y);
7028
7029     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7030       MovDir[x][y] = left_dir;
7031     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7032       MovDir[x][y] = right_dir;
7033
7034     if (MovDir[x][y] != old_move_dir)
7035       MovDelay[x][y] = 9;
7036   }
7037   else if (element == EL_YAMYAM)
7038   {
7039     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7040     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7041
7042     if (can_turn_left && can_turn_right)
7043       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7044     else if (can_turn_left)
7045       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7046     else if (can_turn_right)
7047       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7048     else
7049       MovDir[x][y] = back_dir;
7050
7051     MovDelay[x][y] = 16 + 16 * RND(3);
7052   }
7053   else if (element == EL_DARK_YAMYAM)
7054   {
7055     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7056                                                          left_x, left_y);
7057     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7058                                                          right_x, right_y);
7059
7060     if (can_turn_left && can_turn_right)
7061       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7062     else if (can_turn_left)
7063       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7064     else if (can_turn_right)
7065       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7066     else
7067       MovDir[x][y] = back_dir;
7068
7069     MovDelay[x][y] = 16 + 16 * RND(3);
7070   }
7071   else if (element == EL_PACMAN)
7072   {
7073     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7074     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7075
7076     if (can_turn_left && can_turn_right)
7077       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7078     else if (can_turn_left)
7079       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7080     else if (can_turn_right)
7081       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7082     else
7083       MovDir[x][y] = back_dir;
7084
7085     MovDelay[x][y] = 6 + RND(40);
7086   }
7087   else if (element == EL_PIG)
7088   {
7089     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7090     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7091     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7092     boolean should_turn_left, should_turn_right, should_move_on;
7093     int rnd_value = 24;
7094     int rnd = RND(rnd_value);
7095
7096     should_turn_left = (can_turn_left &&
7097                         (!can_move_on ||
7098                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7099                                                    y + back_dy + left_dy)));
7100     should_turn_right = (can_turn_right &&
7101                          (!can_move_on ||
7102                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7103                                                     y + back_dy + right_dy)));
7104     should_move_on = (can_move_on &&
7105                       (!can_turn_left ||
7106                        !can_turn_right ||
7107                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7108                                                  y + move_dy + left_dy) ||
7109                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7110                                                  y + move_dy + right_dy)));
7111
7112     if (should_turn_left || should_turn_right || should_move_on)
7113     {
7114       if (should_turn_left && should_turn_right && should_move_on)
7115         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7116                         rnd < 2 * rnd_value / 3 ? right_dir :
7117                         old_move_dir);
7118       else if (should_turn_left && should_turn_right)
7119         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7120       else if (should_turn_left && should_move_on)
7121         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7122       else if (should_turn_right && should_move_on)
7123         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7124       else if (should_turn_left)
7125         MovDir[x][y] = left_dir;
7126       else if (should_turn_right)
7127         MovDir[x][y] = right_dir;
7128       else if (should_move_on)
7129         MovDir[x][y] = old_move_dir;
7130     }
7131     else if (can_move_on && rnd > rnd_value / 8)
7132       MovDir[x][y] = old_move_dir;
7133     else if (can_turn_left && can_turn_right)
7134       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7135     else if (can_turn_left && rnd > rnd_value / 8)
7136       MovDir[x][y] = left_dir;
7137     else if (can_turn_right && rnd > rnd_value/8)
7138       MovDir[x][y] = right_dir;
7139     else
7140       MovDir[x][y] = back_dir;
7141
7142     xx = x + move_xy[MovDir[x][y]].dx;
7143     yy = y + move_xy[MovDir[x][y]].dy;
7144
7145     if (!IN_LEV_FIELD(xx, yy) ||
7146         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7147       MovDir[x][y] = old_move_dir;
7148
7149     MovDelay[x][y] = 0;
7150   }
7151   else if (element == EL_DRAGON)
7152   {
7153     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7154     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7155     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7156     int rnd_value = 24;
7157     int rnd = RND(rnd_value);
7158
7159     if (can_move_on && rnd > rnd_value / 8)
7160       MovDir[x][y] = old_move_dir;
7161     else if (can_turn_left && can_turn_right)
7162       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7163     else if (can_turn_left && rnd > rnd_value / 8)
7164       MovDir[x][y] = left_dir;
7165     else if (can_turn_right && rnd > rnd_value / 8)
7166       MovDir[x][y] = right_dir;
7167     else
7168       MovDir[x][y] = back_dir;
7169
7170     xx = x + move_xy[MovDir[x][y]].dx;
7171     yy = y + move_xy[MovDir[x][y]].dy;
7172
7173     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7174       MovDir[x][y] = old_move_dir;
7175
7176     MovDelay[x][y] = 0;
7177   }
7178   else if (element == EL_MOLE)
7179   {
7180     boolean can_move_on =
7181       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7182                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7183                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7184     if (!can_move_on)
7185     {
7186       boolean can_turn_left =
7187         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7188                               IS_AMOEBOID(Tile[left_x][left_y])));
7189
7190       boolean can_turn_right =
7191         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7192                               IS_AMOEBOID(Tile[right_x][right_y])));
7193
7194       if (can_turn_left && can_turn_right)
7195         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7196       else if (can_turn_left)
7197         MovDir[x][y] = left_dir;
7198       else
7199         MovDir[x][y] = right_dir;
7200     }
7201
7202     if (MovDir[x][y] != old_move_dir)
7203       MovDelay[x][y] = 9;
7204   }
7205   else if (element == EL_BALLOON)
7206   {
7207     MovDir[x][y] = game.wind_direction;
7208     MovDelay[x][y] = 0;
7209   }
7210   else if (element == EL_SPRING)
7211   {
7212     if (MovDir[x][y] & MV_HORIZONTAL)
7213     {
7214       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7215           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7216       {
7217         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7218         ResetGfxAnimation(move_x, move_y);
7219         TEST_DrawLevelField(move_x, move_y);
7220
7221         MovDir[x][y] = back_dir;
7222       }
7223       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7224                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7225         MovDir[x][y] = MV_NONE;
7226     }
7227
7228     MovDelay[x][y] = 0;
7229   }
7230   else if (element == EL_ROBOT ||
7231            element == EL_SATELLITE ||
7232            element == EL_PENGUIN ||
7233            element == EL_EMC_ANDROID)
7234   {
7235     int attr_x = -1, attr_y = -1;
7236
7237     if (game.all_players_gone)
7238     {
7239       attr_x = game.exit_x;
7240       attr_y = game.exit_y;
7241     }
7242     else
7243     {
7244       int i;
7245
7246       for (i = 0; i < MAX_PLAYERS; i++)
7247       {
7248         struct PlayerInfo *player = &stored_player[i];
7249         int jx = player->jx, jy = player->jy;
7250
7251         if (!player->active)
7252           continue;
7253
7254         if (attr_x == -1 ||
7255             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7256         {
7257           attr_x = jx;
7258           attr_y = jy;
7259         }
7260       }
7261     }
7262
7263     if (element == EL_ROBOT &&
7264         game.robot_wheel_x >= 0 &&
7265         game.robot_wheel_y >= 0 &&
7266         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7267          game.engine_version < VERSION_IDENT(3,1,0,0)))
7268     {
7269       attr_x = game.robot_wheel_x;
7270       attr_y = game.robot_wheel_y;
7271     }
7272
7273     if (element == EL_PENGUIN)
7274     {
7275       int i;
7276       struct XY *xy = xy_topdown;
7277
7278       for (i = 0; i < NUM_DIRECTIONS; i++)
7279       {
7280         int ex = x + xy[i].x;
7281         int ey = y + xy[i].y;
7282
7283         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7284                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7285                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7286                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7287         {
7288           attr_x = ex;
7289           attr_y = ey;
7290           break;
7291         }
7292       }
7293     }
7294
7295     MovDir[x][y] = MV_NONE;
7296     if (attr_x < x)
7297       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7298     else if (attr_x > x)
7299       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7300     if (attr_y < y)
7301       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7302     else if (attr_y > y)
7303       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7304
7305     if (element == EL_ROBOT)
7306     {
7307       int newx, newy;
7308
7309       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7310         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7311       Moving2Blocked(x, y, &newx, &newy);
7312
7313       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7314         MovDelay[x][y] = 8 + 8 * !RND(3);
7315       else
7316         MovDelay[x][y] = 16;
7317     }
7318     else if (element == EL_PENGUIN)
7319     {
7320       int newx, newy;
7321
7322       MovDelay[x][y] = 1;
7323
7324       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7325       {
7326         boolean first_horiz = RND(2);
7327         int new_move_dir = MovDir[x][y];
7328
7329         MovDir[x][y] =
7330           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7331         Moving2Blocked(x, y, &newx, &newy);
7332
7333         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7334           return;
7335
7336         MovDir[x][y] =
7337           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7338         Moving2Blocked(x, y, &newx, &newy);
7339
7340         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7341           return;
7342
7343         MovDir[x][y] = old_move_dir;
7344         return;
7345       }
7346     }
7347     else if (element == EL_SATELLITE)
7348     {
7349       int newx, newy;
7350
7351       MovDelay[x][y] = 1;
7352
7353       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7354       {
7355         boolean first_horiz = RND(2);
7356         int new_move_dir = MovDir[x][y];
7357
7358         MovDir[x][y] =
7359           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7360         Moving2Blocked(x, y, &newx, &newy);
7361
7362         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7363           return;
7364
7365         MovDir[x][y] =
7366           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7367         Moving2Blocked(x, y, &newx, &newy);
7368
7369         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7370           return;
7371
7372         MovDir[x][y] = old_move_dir;
7373         return;
7374       }
7375     }
7376     else if (element == EL_EMC_ANDROID)
7377     {
7378       static int check_pos[16] =
7379       {
7380         -1,             //  0 => (invalid)
7381         7,              //  1 => MV_LEFT
7382         3,              //  2 => MV_RIGHT
7383         -1,             //  3 => (invalid)
7384         1,              //  4 =>            MV_UP
7385         0,              //  5 => MV_LEFT  | MV_UP
7386         2,              //  6 => MV_RIGHT | MV_UP
7387         -1,             //  7 => (invalid)
7388         5,              //  8 =>            MV_DOWN
7389         6,              //  9 => MV_LEFT  | MV_DOWN
7390         4,              // 10 => MV_RIGHT | MV_DOWN
7391         -1,             // 11 => (invalid)
7392         -1,             // 12 => (invalid)
7393         -1,             // 13 => (invalid)
7394         -1,             // 14 => (invalid)
7395         -1,             // 15 => (invalid)
7396       };
7397       static struct
7398       {
7399         int dx, dy;
7400         int dir;
7401       } check_xy[8] =
7402       {
7403         { -1, -1,       MV_LEFT  | MV_UP   },
7404         {  0, -1,                  MV_UP   },
7405         { +1, -1,       MV_RIGHT | MV_UP   },
7406         { +1,  0,       MV_RIGHT           },
7407         { +1, +1,       MV_RIGHT | MV_DOWN },
7408         {  0, +1,                  MV_DOWN },
7409         { -1, +1,       MV_LEFT  | MV_DOWN },
7410         { -1,  0,       MV_LEFT            },
7411       };
7412       int start_pos, check_order;
7413       boolean can_clone = FALSE;
7414       int i;
7415
7416       // check if there is any free field around current position
7417       for (i = 0; i < 8; i++)
7418       {
7419         int newx = x + check_xy[i].dx;
7420         int newy = y + check_xy[i].dy;
7421
7422         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7423         {
7424           can_clone = TRUE;
7425
7426           break;
7427         }
7428       }
7429
7430       if (can_clone)            // randomly find an element to clone
7431       {
7432         can_clone = FALSE;
7433
7434         start_pos = check_pos[RND(8)];
7435         check_order = (RND(2) ? -1 : +1);
7436
7437         for (i = 0; i < 8; i++)
7438         {
7439           int pos_raw = start_pos + i * check_order;
7440           int pos = (pos_raw + 8) % 8;
7441           int newx = x + check_xy[pos].dx;
7442           int newy = y + check_xy[pos].dy;
7443
7444           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7445           {
7446             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7447             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7448
7449             Store[x][y] = Tile[newx][newy];
7450
7451             can_clone = TRUE;
7452
7453             break;
7454           }
7455         }
7456       }
7457
7458       if (can_clone)            // randomly find a direction to move
7459       {
7460         can_clone = FALSE;
7461
7462         start_pos = check_pos[RND(8)];
7463         check_order = (RND(2) ? -1 : +1);
7464
7465         for (i = 0; i < 8; i++)
7466         {
7467           int pos_raw = start_pos + i * check_order;
7468           int pos = (pos_raw + 8) % 8;
7469           int newx = x + check_xy[pos].dx;
7470           int newy = y + check_xy[pos].dy;
7471           int new_move_dir = check_xy[pos].dir;
7472
7473           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7474           {
7475             MovDir[x][y] = new_move_dir;
7476             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7477
7478             can_clone = TRUE;
7479
7480             break;
7481           }
7482         }
7483       }
7484
7485       if (can_clone)            // cloning and moving successful
7486         return;
7487
7488       // cannot clone -- try to move towards player
7489
7490       start_pos = check_pos[MovDir[x][y] & 0x0f];
7491       check_order = (RND(2) ? -1 : +1);
7492
7493       for (i = 0; i < 3; i++)
7494       {
7495         // first check start_pos, then previous/next or (next/previous) pos
7496         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7497         int pos = (pos_raw + 8) % 8;
7498         int newx = x + check_xy[pos].dx;
7499         int newy = y + check_xy[pos].dy;
7500         int new_move_dir = check_xy[pos].dir;
7501
7502         if (IS_PLAYER(newx, newy))
7503           break;
7504
7505         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7506         {
7507           MovDir[x][y] = new_move_dir;
7508           MovDelay[x][y] = level.android_move_time * 8 + 1;
7509
7510           break;
7511         }
7512       }
7513     }
7514   }
7515   else if (move_pattern == MV_TURNING_LEFT ||
7516            move_pattern == MV_TURNING_RIGHT ||
7517            move_pattern == MV_TURNING_LEFT_RIGHT ||
7518            move_pattern == MV_TURNING_RIGHT_LEFT ||
7519            move_pattern == MV_TURNING_RANDOM ||
7520            move_pattern == MV_ALL_DIRECTIONS)
7521   {
7522     boolean can_turn_left =
7523       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7524     boolean can_turn_right =
7525       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7526
7527     if (element_info[element].move_stepsize == 0)       // "not moving"
7528       return;
7529
7530     if (move_pattern == MV_TURNING_LEFT)
7531       MovDir[x][y] = left_dir;
7532     else if (move_pattern == MV_TURNING_RIGHT)
7533       MovDir[x][y] = right_dir;
7534     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7535       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7536     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7537       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7538     else if (move_pattern == MV_TURNING_RANDOM)
7539       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7540                       can_turn_right && !can_turn_left ? right_dir :
7541                       RND(2) ? left_dir : right_dir);
7542     else if (can_turn_left && can_turn_right)
7543       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7544     else if (can_turn_left)
7545       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7546     else if (can_turn_right)
7547       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7548     else
7549       MovDir[x][y] = back_dir;
7550
7551     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7552   }
7553   else if (move_pattern == MV_HORIZONTAL ||
7554            move_pattern == MV_VERTICAL)
7555   {
7556     if (move_pattern & old_move_dir)
7557       MovDir[x][y] = back_dir;
7558     else if (move_pattern == MV_HORIZONTAL)
7559       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7560     else if (move_pattern == MV_VERTICAL)
7561       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7562
7563     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7564   }
7565   else if (move_pattern & MV_ANY_DIRECTION)
7566   {
7567     MovDir[x][y] = move_pattern;
7568     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7569   }
7570   else if (move_pattern & MV_WIND_DIRECTION)
7571   {
7572     MovDir[x][y] = game.wind_direction;
7573     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7574   }
7575   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7576   {
7577     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7578       MovDir[x][y] = left_dir;
7579     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7580       MovDir[x][y] = right_dir;
7581
7582     if (MovDir[x][y] != old_move_dir)
7583       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7584   }
7585   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7586   {
7587     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7588       MovDir[x][y] = right_dir;
7589     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7590       MovDir[x][y] = left_dir;
7591
7592     if (MovDir[x][y] != old_move_dir)
7593       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7594   }
7595   else if (move_pattern == MV_TOWARDS_PLAYER ||
7596            move_pattern == MV_AWAY_FROM_PLAYER)
7597   {
7598     int attr_x = -1, attr_y = -1;
7599     int newx, newy;
7600     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7601
7602     if (game.all_players_gone)
7603     {
7604       attr_x = game.exit_x;
7605       attr_y = game.exit_y;
7606     }
7607     else
7608     {
7609       int i;
7610
7611       for (i = 0; i < MAX_PLAYERS; i++)
7612       {
7613         struct PlayerInfo *player = &stored_player[i];
7614         int jx = player->jx, jy = player->jy;
7615
7616         if (!player->active)
7617           continue;
7618
7619         if (attr_x == -1 ||
7620             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7621         {
7622           attr_x = jx;
7623           attr_y = jy;
7624         }
7625       }
7626     }
7627
7628     MovDir[x][y] = MV_NONE;
7629     if (attr_x < x)
7630       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7631     else if (attr_x > x)
7632       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7633     if (attr_y < y)
7634       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7635     else if (attr_y > y)
7636       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7637
7638     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7639
7640     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7641     {
7642       boolean first_horiz = RND(2);
7643       int new_move_dir = MovDir[x][y];
7644
7645       if (element_info[element].move_stepsize == 0)     // "not moving"
7646       {
7647         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7648         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7649
7650         return;
7651       }
7652
7653       MovDir[x][y] =
7654         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7655       Moving2Blocked(x, y, &newx, &newy);
7656
7657       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7658         return;
7659
7660       MovDir[x][y] =
7661         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7662       Moving2Blocked(x, y, &newx, &newy);
7663
7664       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7665         return;
7666
7667       MovDir[x][y] = old_move_dir;
7668     }
7669   }
7670   else if (move_pattern == MV_WHEN_PUSHED ||
7671            move_pattern == MV_WHEN_DROPPED)
7672   {
7673     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7674       MovDir[x][y] = MV_NONE;
7675
7676     MovDelay[x][y] = 0;
7677   }
7678   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7679   {
7680     struct XY *test_xy = xy_topdown;
7681     static int test_dir[4] =
7682     {
7683       MV_UP,
7684       MV_LEFT,
7685       MV_RIGHT,
7686       MV_DOWN
7687     };
7688     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7689     int move_preference = -1000000;     // start with very low preference
7690     int new_move_dir = MV_NONE;
7691     int start_test = RND(4);
7692     int i;
7693
7694     for (i = 0; i < NUM_DIRECTIONS; i++)
7695     {
7696       int j = (start_test + i) % 4;
7697       int move_dir = test_dir[j];
7698       int move_dir_preference;
7699
7700       xx = x + test_xy[j].x;
7701       yy = y + test_xy[j].y;
7702
7703       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7704           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7705       {
7706         new_move_dir = move_dir;
7707
7708         break;
7709       }
7710
7711       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7712         continue;
7713
7714       move_dir_preference = -1 * RunnerVisit[xx][yy];
7715       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7716         move_dir_preference = PlayerVisit[xx][yy];
7717
7718       if (move_dir_preference > move_preference)
7719       {
7720         // prefer field that has not been visited for the longest time
7721         move_preference = move_dir_preference;
7722         new_move_dir = move_dir;
7723       }
7724       else if (move_dir_preference == move_preference &&
7725                move_dir == old_move_dir)
7726       {
7727         // prefer last direction when all directions are preferred equally
7728         move_preference = move_dir_preference;
7729         new_move_dir = move_dir;
7730       }
7731     }
7732
7733     MovDir[x][y] = new_move_dir;
7734     if (old_move_dir != new_move_dir)
7735       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7736   }
7737 }
7738
7739 static void TurnRound(int x, int y)
7740 {
7741   int direction = MovDir[x][y];
7742
7743   TurnRoundExt(x, y);
7744
7745   GfxDir[x][y] = MovDir[x][y];
7746
7747   if (direction != MovDir[x][y])
7748     GfxFrame[x][y] = 0;
7749
7750   if (MovDelay[x][y])
7751     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7752
7753   ResetGfxFrame(x, y);
7754 }
7755
7756 static boolean JustBeingPushed(int x, int y)
7757 {
7758   int i;
7759
7760   for (i = 0; i < MAX_PLAYERS; i++)
7761   {
7762     struct PlayerInfo *player = &stored_player[i];
7763
7764     if (player->active && player->is_pushing && player->MovPos)
7765     {
7766       int next_jx = player->jx + (player->jx - player->last_jx);
7767       int next_jy = player->jy + (player->jy - player->last_jy);
7768
7769       if (x == next_jx && y == next_jy)
7770         return TRUE;
7771     }
7772   }
7773
7774   return FALSE;
7775 }
7776
7777 static void StartMoving(int x, int y)
7778 {
7779   boolean started_moving = FALSE;       // some elements can fall _and_ move
7780   int element = Tile[x][y];
7781
7782   if (Stop[x][y])
7783     return;
7784
7785   if (MovDelay[x][y] == 0)
7786     GfxAction[x][y] = ACTION_DEFAULT;
7787
7788   if (CAN_FALL(element) && y < lev_fieldy - 1)
7789   {
7790     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7791         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7792       if (JustBeingPushed(x, y))
7793         return;
7794
7795     if (element == EL_QUICKSAND_FULL)
7796     {
7797       if (IS_FREE(x, y + 1))
7798       {
7799         InitMovingField(x, y, MV_DOWN);
7800         started_moving = TRUE;
7801
7802         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7803 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7804         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7805           Store[x][y] = EL_ROCK;
7806 #else
7807         Store[x][y] = EL_ROCK;
7808 #endif
7809
7810         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7811       }
7812       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7813       {
7814         if (!MovDelay[x][y])
7815         {
7816           MovDelay[x][y] = TILEY + 1;
7817
7818           ResetGfxAnimation(x, y);
7819           ResetGfxAnimation(x, y + 1);
7820         }
7821
7822         if (MovDelay[x][y])
7823         {
7824           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7825           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7826
7827           MovDelay[x][y]--;
7828           if (MovDelay[x][y])
7829             return;
7830         }
7831
7832         Tile[x][y] = EL_QUICKSAND_EMPTY;
7833         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7834         Store[x][y + 1] = Store[x][y];
7835         Store[x][y] = 0;
7836
7837         PlayLevelSoundAction(x, y, ACTION_FILLING);
7838       }
7839       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7840       {
7841         if (!MovDelay[x][y])
7842         {
7843           MovDelay[x][y] = TILEY + 1;
7844
7845           ResetGfxAnimation(x, y);
7846           ResetGfxAnimation(x, y + 1);
7847         }
7848
7849         if (MovDelay[x][y])
7850         {
7851           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7852           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7853
7854           MovDelay[x][y]--;
7855           if (MovDelay[x][y])
7856             return;
7857         }
7858
7859         Tile[x][y] = EL_QUICKSAND_EMPTY;
7860         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7861         Store[x][y + 1] = Store[x][y];
7862         Store[x][y] = 0;
7863
7864         PlayLevelSoundAction(x, y, ACTION_FILLING);
7865       }
7866     }
7867     else if (element == EL_QUICKSAND_FAST_FULL)
7868     {
7869       if (IS_FREE(x, y + 1))
7870       {
7871         InitMovingField(x, y, MV_DOWN);
7872         started_moving = TRUE;
7873
7874         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7875 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7876         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7877           Store[x][y] = EL_ROCK;
7878 #else
7879         Store[x][y] = EL_ROCK;
7880 #endif
7881
7882         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7883       }
7884       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7885       {
7886         if (!MovDelay[x][y])
7887         {
7888           MovDelay[x][y] = TILEY + 1;
7889
7890           ResetGfxAnimation(x, y);
7891           ResetGfxAnimation(x, y + 1);
7892         }
7893
7894         if (MovDelay[x][y])
7895         {
7896           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7897           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7898
7899           MovDelay[x][y]--;
7900           if (MovDelay[x][y])
7901             return;
7902         }
7903
7904         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7905         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7906         Store[x][y + 1] = Store[x][y];
7907         Store[x][y] = 0;
7908
7909         PlayLevelSoundAction(x, y, ACTION_FILLING);
7910       }
7911       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7912       {
7913         if (!MovDelay[x][y])
7914         {
7915           MovDelay[x][y] = TILEY + 1;
7916
7917           ResetGfxAnimation(x, y);
7918           ResetGfxAnimation(x, y + 1);
7919         }
7920
7921         if (MovDelay[x][y])
7922         {
7923           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7924           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7925
7926           MovDelay[x][y]--;
7927           if (MovDelay[x][y])
7928             return;
7929         }
7930
7931         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7932         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7933         Store[x][y + 1] = Store[x][y];
7934         Store[x][y] = 0;
7935
7936         PlayLevelSoundAction(x, y, ACTION_FILLING);
7937       }
7938     }
7939     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7940              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7941     {
7942       InitMovingField(x, y, MV_DOWN);
7943       started_moving = TRUE;
7944
7945       Tile[x][y] = EL_QUICKSAND_FILLING;
7946       Store[x][y] = element;
7947
7948       PlayLevelSoundAction(x, y, ACTION_FILLING);
7949     }
7950     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7951              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7952     {
7953       InitMovingField(x, y, MV_DOWN);
7954       started_moving = TRUE;
7955
7956       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7957       Store[x][y] = element;
7958
7959       PlayLevelSoundAction(x, y, ACTION_FILLING);
7960     }
7961     else if (element == EL_MAGIC_WALL_FULL)
7962     {
7963       if (IS_FREE(x, y + 1))
7964       {
7965         InitMovingField(x, y, MV_DOWN);
7966         started_moving = TRUE;
7967
7968         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7969         Store[x][y] = EL_CHANGED(Store[x][y]);
7970       }
7971       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7972       {
7973         if (!MovDelay[x][y])
7974           MovDelay[x][y] = TILEY / 4 + 1;
7975
7976         if (MovDelay[x][y])
7977         {
7978           MovDelay[x][y]--;
7979           if (MovDelay[x][y])
7980             return;
7981         }
7982
7983         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7984         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7985         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7986         Store[x][y] = 0;
7987       }
7988     }
7989     else if (element == EL_BD_MAGIC_WALL_FULL)
7990     {
7991       if (IS_FREE(x, y + 1))
7992       {
7993         InitMovingField(x, y, MV_DOWN);
7994         started_moving = TRUE;
7995
7996         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7997         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7998       }
7999       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8000       {
8001         if (!MovDelay[x][y])
8002           MovDelay[x][y] = TILEY / 4 + 1;
8003
8004         if (MovDelay[x][y])
8005         {
8006           MovDelay[x][y]--;
8007           if (MovDelay[x][y])
8008             return;
8009         }
8010
8011         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8012         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8013         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8014         Store[x][y] = 0;
8015       }
8016     }
8017     else if (element == EL_DC_MAGIC_WALL_FULL)
8018     {
8019       if (IS_FREE(x, y + 1))
8020       {
8021         InitMovingField(x, y, MV_DOWN);
8022         started_moving = TRUE;
8023
8024         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8025         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8026       }
8027       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8028       {
8029         if (!MovDelay[x][y])
8030           MovDelay[x][y] = TILEY / 4 + 1;
8031
8032         if (MovDelay[x][y])
8033         {
8034           MovDelay[x][y]--;
8035           if (MovDelay[x][y])
8036             return;
8037         }
8038
8039         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8040         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8041         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8042         Store[x][y] = 0;
8043       }
8044     }
8045     else if ((CAN_PASS_MAGIC_WALL(element) &&
8046               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8047                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8048              (CAN_PASS_DC_MAGIC_WALL(element) &&
8049               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8050
8051     {
8052       InitMovingField(x, y, MV_DOWN);
8053       started_moving = TRUE;
8054
8055       Tile[x][y] =
8056         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8057          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8058          EL_DC_MAGIC_WALL_FILLING);
8059       Store[x][y] = element;
8060     }
8061     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8062     {
8063       SplashAcid(x, y + 1);
8064
8065       InitMovingField(x, y, MV_DOWN);
8066       started_moving = TRUE;
8067
8068       Store[x][y] = EL_ACID;
8069     }
8070     else if (
8071              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8072               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8073              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8074               CAN_FALL(element) && WasJustFalling[x][y] &&
8075               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8076
8077              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8078               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8079               (Tile[x][y + 1] == EL_BLOCKED)))
8080     {
8081       /* this is needed for a special case not covered by calling "Impact()"
8082          from "ContinueMoving()": if an element moves to a tile directly below
8083          another element which was just falling on that tile (which was empty
8084          in the previous frame), the falling element above would just stop
8085          instead of smashing the element below (in previous version, the above
8086          element was just checked for "moving" instead of "falling", resulting
8087          in incorrect smashes caused by horizontal movement of the above
8088          element; also, the case of the player being the element to smash was
8089          simply not covered here... :-/ ) */
8090
8091       CheckCollision[x][y] = 0;
8092       CheckImpact[x][y] = 0;
8093
8094       Impact(x, y);
8095     }
8096     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8097     {
8098       if (MovDir[x][y] == MV_NONE)
8099       {
8100         InitMovingField(x, y, MV_DOWN);
8101         started_moving = TRUE;
8102       }
8103     }
8104     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8105     {
8106       if (WasJustFalling[x][y]) // prevent animation from being restarted
8107         MovDir[x][y] = MV_DOWN;
8108
8109       InitMovingField(x, y, MV_DOWN);
8110       started_moving = TRUE;
8111     }
8112     else if (element == EL_AMOEBA_DROP)
8113     {
8114       Tile[x][y] = EL_AMOEBA_GROWING;
8115       Store[x][y] = EL_AMOEBA_WET;
8116     }
8117     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8118               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8119              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8120              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8121     {
8122       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8123                                 (IS_FREE(x - 1, y + 1) ||
8124                                  Tile[x - 1][y + 1] == EL_ACID));
8125       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8126                                 (IS_FREE(x + 1, y + 1) ||
8127                                  Tile[x + 1][y + 1] == EL_ACID));
8128       boolean can_fall_any  = (can_fall_left || can_fall_right);
8129       boolean can_fall_both = (can_fall_left && can_fall_right);
8130       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8131
8132       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8133       {
8134         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8135           can_fall_right = FALSE;
8136         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8137           can_fall_left = FALSE;
8138         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8139           can_fall_right = FALSE;
8140         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8141           can_fall_left = FALSE;
8142
8143         can_fall_any  = (can_fall_left || can_fall_right);
8144         can_fall_both = FALSE;
8145       }
8146
8147       if (can_fall_both)
8148       {
8149         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8150           can_fall_right = FALSE;       // slip down on left side
8151         else
8152           can_fall_left = !(can_fall_right = RND(2));
8153
8154         can_fall_both = FALSE;
8155       }
8156
8157       if (can_fall_any)
8158       {
8159         // if not determined otherwise, prefer left side for slipping down
8160         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8161         started_moving = TRUE;
8162       }
8163     }
8164     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8165     {
8166       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8167       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8168       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8169       int belt_dir = game.belt_dir[belt_nr];
8170
8171       if ((belt_dir == MV_LEFT  && left_is_free) ||
8172           (belt_dir == MV_RIGHT && right_is_free))
8173       {
8174         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8175
8176         InitMovingField(x, y, belt_dir);
8177         started_moving = TRUE;
8178
8179         Pushed[x][y] = TRUE;
8180         Pushed[nextx][y] = TRUE;
8181
8182         GfxAction[x][y] = ACTION_DEFAULT;
8183       }
8184       else
8185       {
8186         MovDir[x][y] = 0;       // if element was moving, stop it
8187       }
8188     }
8189   }
8190
8191   // not "else if" because of elements that can fall and move (EL_SPRING)
8192   if (CAN_MOVE(element) && !started_moving)
8193   {
8194     int move_pattern = element_info[element].move_pattern;
8195     int newx, newy;
8196
8197     Moving2Blocked(x, y, &newx, &newy);
8198
8199     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8200       return;
8201
8202     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8203         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8204     {
8205       WasJustMoving[x][y] = 0;
8206       CheckCollision[x][y] = 0;
8207
8208       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8209
8210       if (Tile[x][y] != element)        // element has changed
8211         return;
8212     }
8213
8214     if (!MovDelay[x][y])        // start new movement phase
8215     {
8216       // all objects that can change their move direction after each step
8217       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8218
8219       if (element != EL_YAMYAM &&
8220           element != EL_DARK_YAMYAM &&
8221           element != EL_PACMAN &&
8222           !(move_pattern & MV_ANY_DIRECTION) &&
8223           move_pattern != MV_TURNING_LEFT &&
8224           move_pattern != MV_TURNING_RIGHT &&
8225           move_pattern != MV_TURNING_LEFT_RIGHT &&
8226           move_pattern != MV_TURNING_RIGHT_LEFT &&
8227           move_pattern != MV_TURNING_RANDOM)
8228       {
8229         TurnRound(x, y);
8230
8231         if (MovDelay[x][y] && (element == EL_BUG ||
8232                                element == EL_SPACESHIP ||
8233                                element == EL_SP_SNIKSNAK ||
8234                                element == EL_SP_ELECTRON ||
8235                                element == EL_MOLE))
8236           TEST_DrawLevelField(x, y);
8237       }
8238     }
8239
8240     if (MovDelay[x][y])         // wait some time before next movement
8241     {
8242       MovDelay[x][y]--;
8243
8244       if (element == EL_ROBOT ||
8245           element == EL_YAMYAM ||
8246           element == EL_DARK_YAMYAM)
8247       {
8248         DrawLevelElementAnimationIfNeeded(x, y, element);
8249         PlayLevelSoundAction(x, y, ACTION_WAITING);
8250       }
8251       else if (element == EL_SP_ELECTRON)
8252         DrawLevelElementAnimationIfNeeded(x, y, element);
8253       else if (element == EL_DRAGON)
8254       {
8255         int i;
8256         int dir = MovDir[x][y];
8257         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8258         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8259         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8260                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8261                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8262                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8263         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8264
8265         GfxAction[x][y] = ACTION_ATTACKING;
8266
8267         if (IS_PLAYER(x, y))
8268           DrawPlayerField(x, y);
8269         else
8270           TEST_DrawLevelField(x, y);
8271
8272         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8273
8274         for (i = 1; i <= 3; i++)
8275         {
8276           int xx = x + i * dx;
8277           int yy = y + i * dy;
8278           int sx = SCREENX(xx);
8279           int sy = SCREENY(yy);
8280           int flame_graphic = graphic + (i - 1);
8281
8282           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8283             break;
8284
8285           if (MovDelay[x][y])
8286           {
8287             int flamed = MovingOrBlocked2Element(xx, yy);
8288
8289             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8290               Bang(xx, yy);
8291             else
8292               RemoveMovingField(xx, yy);
8293
8294             ChangeDelay[xx][yy] = 0;
8295
8296             Tile[xx][yy] = EL_FLAMES;
8297
8298             if (IN_SCR_FIELD(sx, sy))
8299             {
8300               TEST_DrawLevelFieldCrumbled(xx, yy);
8301               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8302             }
8303           }
8304           else
8305           {
8306             if (Tile[xx][yy] == EL_FLAMES)
8307               Tile[xx][yy] = EL_EMPTY;
8308             TEST_DrawLevelField(xx, yy);
8309           }
8310         }
8311       }
8312
8313       if (MovDelay[x][y])       // element still has to wait some time
8314       {
8315         PlayLevelSoundAction(x, y, ACTION_WAITING);
8316
8317         return;
8318       }
8319     }
8320
8321     // now make next step
8322
8323     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8324
8325     if (DONT_COLLIDE_WITH(element) &&
8326         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8327         !PLAYER_ENEMY_PROTECTED(newx, newy))
8328     {
8329       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8330
8331       return;
8332     }
8333
8334     else if (CAN_MOVE_INTO_ACID(element) &&
8335              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8336              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8337              (MovDir[x][y] == MV_DOWN ||
8338               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8339     {
8340       SplashAcid(newx, newy);
8341       Store[x][y] = EL_ACID;
8342     }
8343     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8344     {
8345       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8346           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8347           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8348           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8349       {
8350         RemoveField(x, y);
8351         TEST_DrawLevelField(x, y);
8352
8353         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8354         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8355           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8356
8357         game.friends_still_needed--;
8358         if (!game.friends_still_needed &&
8359             !game.GameOver &&
8360             game.all_players_gone)
8361           LevelSolved();
8362
8363         return;
8364       }
8365       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8366       {
8367         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8368           TEST_DrawLevelField(newx, newy);
8369         else
8370           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8371       }
8372       else if (!IS_FREE(newx, newy))
8373       {
8374         GfxAction[x][y] = ACTION_WAITING;
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_PIG && IN_LEV_FIELD(newx, newy))
8385     {
8386       if (IS_FOOD_PIG(Tile[newx][newy]))
8387       {
8388         if (IS_MOVING(newx, newy))
8389           RemoveMovingField(newx, newy);
8390         else
8391         {
8392           Tile[newx][newy] = EL_EMPTY;
8393           TEST_DrawLevelField(newx, newy);
8394         }
8395
8396         PlayLevelSound(x, y, SND_PIG_DIGGING);
8397       }
8398       else if (!IS_FREE(newx, newy))
8399       {
8400         if (IS_PLAYER(x, y))
8401           DrawPlayerField(x, y);
8402         else
8403           TEST_DrawLevelField(x, y);
8404
8405         return;
8406       }
8407     }
8408     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8409     {
8410       if (Store[x][y] != EL_EMPTY)
8411       {
8412         boolean can_clone = FALSE;
8413         int xx, yy;
8414
8415         // check if element to clone is still there
8416         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8417         {
8418           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8419           {
8420             can_clone = TRUE;
8421
8422             break;
8423           }
8424         }
8425
8426         // cannot clone or target field not free anymore -- do not clone
8427         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8428           Store[x][y] = EL_EMPTY;
8429       }
8430
8431       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8432       {
8433         if (IS_MV_DIAGONAL(MovDir[x][y]))
8434         {
8435           int diagonal_move_dir = MovDir[x][y];
8436           int stored = Store[x][y];
8437           int change_delay = 8;
8438           int graphic;
8439
8440           // android is moving diagonally
8441
8442           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8443
8444           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8445           GfxElement[x][y] = EL_EMC_ANDROID;
8446           GfxAction[x][y] = ACTION_SHRINKING;
8447           GfxDir[x][y] = diagonal_move_dir;
8448           ChangeDelay[x][y] = change_delay;
8449
8450           if (Store[x][y] == EL_EMPTY)
8451             Store[x][y] = GfxElementEmpty[x][y];
8452
8453           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8454                                    GfxDir[x][y]);
8455
8456           DrawLevelGraphicAnimation(x, y, graphic);
8457           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8458
8459           if (Tile[newx][newy] == EL_ACID)
8460           {
8461             SplashAcid(newx, newy);
8462
8463             return;
8464           }
8465
8466           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8467
8468           Store[newx][newy] = EL_EMC_ANDROID;
8469           GfxElement[newx][newy] = EL_EMC_ANDROID;
8470           GfxAction[newx][newy] = ACTION_GROWING;
8471           GfxDir[newx][newy] = diagonal_move_dir;
8472           ChangeDelay[newx][newy] = change_delay;
8473
8474           graphic = el_act_dir2img(GfxElement[newx][newy],
8475                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8476
8477           DrawLevelGraphicAnimation(newx, newy, graphic);
8478           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8479
8480           return;
8481         }
8482         else
8483         {
8484           Tile[newx][newy] = EL_EMPTY;
8485           TEST_DrawLevelField(newx, newy);
8486
8487           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8488         }
8489       }
8490       else if (!IS_FREE(newx, newy))
8491       {
8492         return;
8493       }
8494     }
8495     else if (IS_CUSTOM_ELEMENT(element) &&
8496              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8497     {
8498       if (!DigFieldByCE(newx, newy, element))
8499         return;
8500
8501       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8502       {
8503         RunnerVisit[x][y] = FrameCounter;
8504         PlayerVisit[x][y] /= 8;         // expire player visit path
8505       }
8506     }
8507     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8508     {
8509       if (!IS_FREE(newx, newy))
8510       {
8511         if (IS_PLAYER(x, y))
8512           DrawPlayerField(x, y);
8513         else
8514           TEST_DrawLevelField(x, y);
8515
8516         return;
8517       }
8518       else
8519       {
8520         boolean wanna_flame = !RND(10);
8521         int dx = newx - x, dy = newy - y;
8522         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8523         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8524         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8525                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8526         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8527                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8528
8529         if ((wanna_flame ||
8530              IS_CLASSIC_ENEMY(element1) ||
8531              IS_CLASSIC_ENEMY(element2)) &&
8532             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8533             element1 != EL_FLAMES && element2 != EL_FLAMES)
8534         {
8535           ResetGfxAnimation(x, y);
8536           GfxAction[x][y] = ACTION_ATTACKING;
8537
8538           if (IS_PLAYER(x, y))
8539             DrawPlayerField(x, y);
8540           else
8541             TEST_DrawLevelField(x, y);
8542
8543           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8544
8545           MovDelay[x][y] = 50;
8546
8547           Tile[newx][newy] = EL_FLAMES;
8548           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8549             Tile[newx1][newy1] = EL_FLAMES;
8550           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8551             Tile[newx2][newy2] = EL_FLAMES;
8552
8553           return;
8554         }
8555       }
8556     }
8557     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8558              Tile[newx][newy] == EL_DIAMOND)
8559     {
8560       if (IS_MOVING(newx, newy))
8561         RemoveMovingField(newx, newy);
8562       else
8563       {
8564         Tile[newx][newy] = EL_EMPTY;
8565         TEST_DrawLevelField(newx, newy);
8566       }
8567
8568       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8569     }
8570     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8571              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8572     {
8573       if (AmoebaNr[newx][newy])
8574       {
8575         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8576         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8577             Tile[newx][newy] == EL_BD_AMOEBA)
8578           AmoebaCnt[AmoebaNr[newx][newy]]--;
8579       }
8580
8581       if (IS_MOVING(newx, newy))
8582       {
8583         RemoveMovingField(newx, newy);
8584       }
8585       else
8586       {
8587         Tile[newx][newy] = EL_EMPTY;
8588         TEST_DrawLevelField(newx, newy);
8589       }
8590
8591       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8592     }
8593     else if ((element == EL_PACMAN || element == EL_MOLE)
8594              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8595     {
8596       if (AmoebaNr[newx][newy])
8597       {
8598         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8599         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8600             Tile[newx][newy] == EL_BD_AMOEBA)
8601           AmoebaCnt[AmoebaNr[newx][newy]]--;
8602       }
8603
8604       if (element == EL_MOLE)
8605       {
8606         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8607         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8608
8609         ResetGfxAnimation(x, y);
8610         GfxAction[x][y] = ACTION_DIGGING;
8611         TEST_DrawLevelField(x, y);
8612
8613         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8614
8615         return;                         // wait for shrinking amoeba
8616       }
8617       else      // element == EL_PACMAN
8618       {
8619         Tile[newx][newy] = EL_EMPTY;
8620         TEST_DrawLevelField(newx, newy);
8621         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8622       }
8623     }
8624     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8625              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8626               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8627     {
8628       // wait for shrinking amoeba to completely disappear
8629       return;
8630     }
8631     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8632     {
8633       // object was running against a wall
8634
8635       TurnRound(x, y);
8636
8637       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8638         DrawLevelElementAnimation(x, y, element);
8639
8640       if (DONT_TOUCH(element))
8641         TestIfBadThingTouchesPlayer(x, y);
8642
8643       return;
8644     }
8645
8646     InitMovingField(x, y, MovDir[x][y]);
8647
8648     PlayLevelSoundAction(x, y, ACTION_MOVING);
8649   }
8650
8651   if (MovDir[x][y])
8652     ContinueMoving(x, y);
8653 }
8654
8655 void ContinueMoving(int x, int y)
8656 {
8657   int element = Tile[x][y];
8658   struct ElementInfo *ei = &element_info[element];
8659   int direction = MovDir[x][y];
8660   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8661   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8662   int newx = x + dx, newy = y + dy;
8663   int stored = Store[x][y];
8664   int stored_new = Store[newx][newy];
8665   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8666   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8667   boolean last_line = (newy == lev_fieldy - 1);
8668   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8669
8670   if (pushed_by_player)         // special case: moving object pushed by player
8671   {
8672     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8673   }
8674   else if (use_step_delay)      // special case: moving object has step delay
8675   {
8676     if (!MovDelay[x][y])
8677       MovPos[x][y] += getElementMoveStepsize(x, y);
8678
8679     if (MovDelay[x][y])
8680       MovDelay[x][y]--;
8681     else
8682       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8683
8684     if (MovDelay[x][y])
8685     {
8686       TEST_DrawLevelField(x, y);
8687
8688       return;   // element is still waiting
8689     }
8690   }
8691   else                          // normal case: generically moving object
8692   {
8693     MovPos[x][y] += getElementMoveStepsize(x, y);
8694   }
8695
8696   if (ABS(MovPos[x][y]) < TILEX)
8697   {
8698     TEST_DrawLevelField(x, y);
8699
8700     return;     // element is still moving
8701   }
8702
8703   // element reached destination field
8704
8705   Tile[x][y] = EL_EMPTY;
8706   Tile[newx][newy] = element;
8707   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8708
8709   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8710   {
8711     element = Tile[newx][newy] = EL_ACID;
8712   }
8713   else if (element == EL_MOLE)
8714   {
8715     Tile[x][y] = EL_SAND;
8716
8717     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8718   }
8719   else if (element == EL_QUICKSAND_FILLING)
8720   {
8721     element = Tile[newx][newy] = get_next_element(element);
8722     Store[newx][newy] = Store[x][y];
8723   }
8724   else if (element == EL_QUICKSAND_EMPTYING)
8725   {
8726     Tile[x][y] = get_next_element(element);
8727     element = Tile[newx][newy] = Store[x][y];
8728   }
8729   else if (element == EL_QUICKSAND_FAST_FILLING)
8730   {
8731     element = Tile[newx][newy] = get_next_element(element);
8732     Store[newx][newy] = Store[x][y];
8733   }
8734   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8735   {
8736     Tile[x][y] = get_next_element(element);
8737     element = Tile[newx][newy] = Store[x][y];
8738   }
8739   else if (element == EL_MAGIC_WALL_FILLING)
8740   {
8741     element = Tile[newx][newy] = get_next_element(element);
8742     if (!game.magic_wall_active)
8743       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8744     Store[newx][newy] = Store[x][y];
8745   }
8746   else if (element == EL_MAGIC_WALL_EMPTYING)
8747   {
8748     Tile[x][y] = get_next_element(element);
8749     if (!game.magic_wall_active)
8750       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8751     element = Tile[newx][newy] = Store[x][y];
8752
8753     InitField(newx, newy, FALSE);
8754   }
8755   else if (element == EL_BD_MAGIC_WALL_FILLING)
8756   {
8757     element = Tile[newx][newy] = get_next_element(element);
8758     if (!game.magic_wall_active)
8759       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8760     Store[newx][newy] = Store[x][y];
8761   }
8762   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8763   {
8764     Tile[x][y] = get_next_element(element);
8765     if (!game.magic_wall_active)
8766       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8767     element = Tile[newx][newy] = Store[x][y];
8768
8769     InitField(newx, newy, FALSE);
8770   }
8771   else if (element == EL_DC_MAGIC_WALL_FILLING)
8772   {
8773     element = Tile[newx][newy] = get_next_element(element);
8774     if (!game.magic_wall_active)
8775       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8776     Store[newx][newy] = Store[x][y];
8777   }
8778   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8779   {
8780     Tile[x][y] = get_next_element(element);
8781     if (!game.magic_wall_active)
8782       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8783     element = Tile[newx][newy] = Store[x][y];
8784
8785     InitField(newx, newy, FALSE);
8786   }
8787   else if (element == EL_AMOEBA_DROPPING)
8788   {
8789     Tile[x][y] = get_next_element(element);
8790     element = Tile[newx][newy] = Store[x][y];
8791   }
8792   else if (element == EL_SOKOBAN_OBJECT)
8793   {
8794     if (Back[x][y])
8795       Tile[x][y] = Back[x][y];
8796
8797     if (Back[newx][newy])
8798       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8799
8800     Back[x][y] = Back[newx][newy] = 0;
8801   }
8802
8803   Store[x][y] = EL_EMPTY;
8804   MovPos[x][y] = 0;
8805   MovDir[x][y] = 0;
8806   MovDelay[x][y] = 0;
8807
8808   MovDelay[newx][newy] = 0;
8809
8810   if (CAN_CHANGE_OR_HAS_ACTION(element))
8811   {
8812     // copy element change control values to new field
8813     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8814     ChangePage[newx][newy]  = ChangePage[x][y];
8815     ChangeCount[newx][newy] = ChangeCount[x][y];
8816     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8817   }
8818
8819   CustomValue[newx][newy] = CustomValue[x][y];
8820
8821   ChangeDelay[x][y] = 0;
8822   ChangePage[x][y] = -1;
8823   ChangeCount[x][y] = 0;
8824   ChangeEvent[x][y] = -1;
8825
8826   CustomValue[x][y] = 0;
8827
8828   // copy animation control values to new field
8829   GfxFrame[newx][newy]  = GfxFrame[x][y];
8830   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8831   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8832   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8833
8834   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8835
8836   // some elements can leave other elements behind after moving
8837   if (ei->move_leave_element != EL_EMPTY &&
8838       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8839       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8840   {
8841     int move_leave_element = ei->move_leave_element;
8842
8843     // this makes it possible to leave the removed element again
8844     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8845       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8846
8847     Tile[x][y] = move_leave_element;
8848
8849     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8850       MovDir[x][y] = direction;
8851
8852     InitField(x, y, FALSE);
8853
8854     if (GFX_CRUMBLED(Tile[x][y]))
8855       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8856
8857     if (IS_PLAYER_ELEMENT(move_leave_element))
8858       RelocatePlayer(x, y, move_leave_element);
8859   }
8860
8861   // do this after checking for left-behind element
8862   ResetGfxAnimation(x, y);      // reset animation values for old field
8863
8864   if (!CAN_MOVE(element) ||
8865       (CAN_FALL(element) && direction == MV_DOWN &&
8866        (element == EL_SPRING ||
8867         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8868         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8869     GfxDir[x][y] = MovDir[newx][newy] = 0;
8870
8871   TEST_DrawLevelField(x, y);
8872   TEST_DrawLevelField(newx, newy);
8873
8874   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8875
8876   // prevent pushed element from moving on in pushed direction
8877   if (pushed_by_player && CAN_MOVE(element) &&
8878       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8879       !(element_info[element].move_pattern & direction))
8880     TurnRound(newx, newy);
8881
8882   // prevent elements on conveyor belt from moving on in last direction
8883   if (pushed_by_conveyor && CAN_FALL(element) &&
8884       direction & MV_HORIZONTAL)
8885     MovDir[newx][newy] = 0;
8886
8887   if (!pushed_by_player)
8888   {
8889     int nextx = newx + dx, nexty = newy + dy;
8890     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8891
8892     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8893
8894     if (CAN_FALL(element) && direction == MV_DOWN)
8895       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8896
8897     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8898       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8899
8900     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8901       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8902   }
8903
8904   if (DONT_TOUCH(element))      // object may be nasty to player or others
8905   {
8906     TestIfBadThingTouchesPlayer(newx, newy);
8907     TestIfBadThingTouchesFriend(newx, newy);
8908
8909     if (!IS_CUSTOM_ELEMENT(element))
8910       TestIfBadThingTouchesOtherBadThing(newx, newy);
8911   }
8912   else if (element == EL_PENGUIN)
8913     TestIfFriendTouchesBadThing(newx, newy);
8914
8915   if (DONT_GET_HIT_BY(element))
8916   {
8917     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8918   }
8919
8920   // give the player one last chance (one more frame) to move away
8921   if (CAN_FALL(element) && direction == MV_DOWN &&
8922       (last_line || (!IS_FREE(x, newy + 1) &&
8923                      (!IS_PLAYER(x, newy + 1) ||
8924                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8925     Impact(x, newy);
8926
8927   if (pushed_by_player && !game.use_change_when_pushing_bug)
8928   {
8929     int push_side = MV_DIR_OPPOSITE(direction);
8930     struct PlayerInfo *player = PLAYERINFO(x, y);
8931
8932     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8933                                player->index_bit, push_side);
8934     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8935                                         player->index_bit, push_side);
8936   }
8937
8938   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8939     MovDelay[newx][newy] = 1;
8940
8941   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8942
8943   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8944   TestIfElementHitsCustomElement(newx, newy, direction);
8945   TestIfPlayerTouchesCustomElement(newx, newy);
8946   TestIfElementTouchesCustomElement(newx, newy);
8947
8948   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8949       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8950     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8951                              MV_DIR_OPPOSITE(direction));
8952 }
8953
8954 int AmoebaNeighbourNr(int ax, int ay)
8955 {
8956   int i;
8957   int element = Tile[ax][ay];
8958   int group_nr = 0;
8959   struct XY *xy = xy_topdown;
8960
8961   for (i = 0; i < NUM_DIRECTIONS; i++)
8962   {
8963     int x = ax + xy[i].x;
8964     int y = ay + xy[i].y;
8965
8966     if (!IN_LEV_FIELD(x, y))
8967       continue;
8968
8969     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8970       group_nr = AmoebaNr[x][y];
8971   }
8972
8973   return group_nr;
8974 }
8975
8976 static void AmoebaMerge(int ax, int ay)
8977 {
8978   int i, x, y, xx, yy;
8979   int new_group_nr = AmoebaNr[ax][ay];
8980   struct XY *xy = xy_topdown;
8981
8982   if (new_group_nr == 0)
8983     return;
8984
8985   for (i = 0; i < NUM_DIRECTIONS; i++)
8986   {
8987     x = ax + xy[i].x;
8988     y = ay + xy[i].y;
8989
8990     if (!IN_LEV_FIELD(x, y))
8991       continue;
8992
8993     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8994          Tile[x][y] == EL_BD_AMOEBA ||
8995          Tile[x][y] == EL_AMOEBA_DEAD) &&
8996         AmoebaNr[x][y] != new_group_nr)
8997     {
8998       int old_group_nr = AmoebaNr[x][y];
8999
9000       if (old_group_nr == 0)
9001         return;
9002
9003       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9004       AmoebaCnt[old_group_nr] = 0;
9005       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9006       AmoebaCnt2[old_group_nr] = 0;
9007
9008       SCAN_PLAYFIELD(xx, yy)
9009       {
9010         if (AmoebaNr[xx][yy] == old_group_nr)
9011           AmoebaNr[xx][yy] = new_group_nr;
9012       }
9013     }
9014   }
9015 }
9016
9017 void AmoebaToDiamond(int ax, int ay)
9018 {
9019   int i, x, y;
9020
9021   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9022   {
9023     int group_nr = AmoebaNr[ax][ay];
9024
9025 #ifdef DEBUG
9026     if (group_nr == 0)
9027     {
9028       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9029       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9030
9031       return;
9032     }
9033 #endif
9034
9035     SCAN_PLAYFIELD(x, y)
9036     {
9037       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9038       {
9039         AmoebaNr[x][y] = 0;
9040         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9041       }
9042     }
9043
9044     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9045                             SND_AMOEBA_TURNING_TO_GEM :
9046                             SND_AMOEBA_TURNING_TO_ROCK));
9047     Bang(ax, ay);
9048   }
9049   else
9050   {
9051     struct XY *xy = xy_topdown;
9052
9053     for (i = 0; i < NUM_DIRECTIONS; i++)
9054     {
9055       x = ax + xy[i].x;
9056       y = ay + xy[i].y;
9057
9058       if (!IN_LEV_FIELD(x, y))
9059         continue;
9060
9061       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9062       {
9063         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9064                               SND_AMOEBA_TURNING_TO_GEM :
9065                               SND_AMOEBA_TURNING_TO_ROCK));
9066         Bang(x, y);
9067       }
9068     }
9069   }
9070 }
9071
9072 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9073 {
9074   int x, y;
9075   int group_nr = AmoebaNr[ax][ay];
9076   boolean done = FALSE;
9077
9078 #ifdef DEBUG
9079   if (group_nr == 0)
9080   {
9081     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9082     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9083
9084     return;
9085   }
9086 #endif
9087
9088   SCAN_PLAYFIELD(x, y)
9089   {
9090     if (AmoebaNr[x][y] == group_nr &&
9091         (Tile[x][y] == EL_AMOEBA_DEAD ||
9092          Tile[x][y] == EL_BD_AMOEBA ||
9093          Tile[x][y] == EL_AMOEBA_GROWING))
9094     {
9095       AmoebaNr[x][y] = 0;
9096       Tile[x][y] = new_element;
9097       InitField(x, y, FALSE);
9098       TEST_DrawLevelField(x, y);
9099       done = TRUE;
9100     }
9101   }
9102
9103   if (done)
9104     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9105                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9106                             SND_BD_AMOEBA_TURNING_TO_GEM));
9107 }
9108
9109 static void AmoebaGrowing(int x, int y)
9110 {
9111   static DelayCounter sound_delay = { 0 };
9112
9113   if (!MovDelay[x][y])          // start new growing cycle
9114   {
9115     MovDelay[x][y] = 7;
9116
9117     if (DelayReached(&sound_delay))
9118     {
9119       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9120       sound_delay.value = 30;
9121     }
9122   }
9123
9124   if (MovDelay[x][y])           // wait some time before growing bigger
9125   {
9126     MovDelay[x][y]--;
9127     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9128     {
9129       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9130                                            6 - MovDelay[x][y]);
9131
9132       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9133     }
9134
9135     if (!MovDelay[x][y])
9136     {
9137       Tile[x][y] = Store[x][y];
9138       Store[x][y] = 0;
9139       TEST_DrawLevelField(x, y);
9140     }
9141   }
9142 }
9143
9144 static void AmoebaShrinking(int x, int y)
9145 {
9146   static DelayCounter sound_delay = { 0 };
9147
9148   if (!MovDelay[x][y])          // start new shrinking cycle
9149   {
9150     MovDelay[x][y] = 7;
9151
9152     if (DelayReached(&sound_delay))
9153       sound_delay.value = 30;
9154   }
9155
9156   if (MovDelay[x][y])           // wait some time before shrinking
9157   {
9158     MovDelay[x][y]--;
9159     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9160     {
9161       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9162                                            6 - MovDelay[x][y]);
9163
9164       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9165     }
9166
9167     if (!MovDelay[x][y])
9168     {
9169       Tile[x][y] = EL_EMPTY;
9170       TEST_DrawLevelField(x, y);
9171
9172       // don't let mole enter this field in this cycle;
9173       // (give priority to objects falling to this field from above)
9174       Stop[x][y] = TRUE;
9175     }
9176   }
9177 }
9178
9179 static void AmoebaReproduce(int ax, int ay)
9180 {
9181   int i;
9182   int element = Tile[ax][ay];
9183   int graphic = el2img(element);
9184   int newax = ax, neway = ay;
9185   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9186   struct XY *xy = xy_topdown;
9187
9188   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9189   {
9190     Tile[ax][ay] = EL_AMOEBA_DEAD;
9191     TEST_DrawLevelField(ax, ay);
9192     return;
9193   }
9194
9195   if (IS_ANIMATED(graphic))
9196     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9197
9198   if (!MovDelay[ax][ay])        // start making new amoeba field
9199     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9200
9201   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9202   {
9203     MovDelay[ax][ay]--;
9204     if (MovDelay[ax][ay])
9205       return;
9206   }
9207
9208   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9209   {
9210     int start = RND(4);
9211     int x = ax + xy[start].x;
9212     int y = ay + xy[start].y;
9213
9214     if (!IN_LEV_FIELD(x, y))
9215       return;
9216
9217     if (IS_FREE(x, y) ||
9218         CAN_GROW_INTO(Tile[x][y]) ||
9219         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9220         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9221     {
9222       newax = x;
9223       neway = y;
9224     }
9225
9226     if (newax == ax && neway == ay)
9227       return;
9228   }
9229   else                          // normal or "filled" (BD style) amoeba
9230   {
9231     int start = RND(4);
9232     boolean waiting_for_player = FALSE;
9233
9234     for (i = 0; i < NUM_DIRECTIONS; i++)
9235     {
9236       int j = (start + i) % 4;
9237       int x = ax + xy[j].x;
9238       int y = ay + xy[j].y;
9239
9240       if (!IN_LEV_FIELD(x, y))
9241         continue;
9242
9243       if (IS_FREE(x, y) ||
9244           CAN_GROW_INTO(Tile[x][y]) ||
9245           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9246           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9247       {
9248         newax = x;
9249         neway = y;
9250         break;
9251       }
9252       else if (IS_PLAYER(x, y))
9253         waiting_for_player = TRUE;
9254     }
9255
9256     if (newax == ax && neway == ay)             // amoeba cannot grow
9257     {
9258       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9259       {
9260         Tile[ax][ay] = EL_AMOEBA_DEAD;
9261         TEST_DrawLevelField(ax, ay);
9262         AmoebaCnt[AmoebaNr[ax][ay]]--;
9263
9264         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9265         {
9266           if (element == EL_AMOEBA_FULL)
9267             AmoebaToDiamond(ax, ay);
9268           else if (element == EL_BD_AMOEBA)
9269             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9270         }
9271       }
9272       return;
9273     }
9274     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9275     {
9276       // amoeba gets larger by growing in some direction
9277
9278       int new_group_nr = AmoebaNr[ax][ay];
9279
9280 #ifdef DEBUG
9281   if (new_group_nr == 0)
9282   {
9283     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9284           newax, neway);
9285     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9286
9287     return;
9288   }
9289 #endif
9290
9291       AmoebaNr[newax][neway] = new_group_nr;
9292       AmoebaCnt[new_group_nr]++;
9293       AmoebaCnt2[new_group_nr]++;
9294
9295       // if amoeba touches other amoeba(s) after growing, unify them
9296       AmoebaMerge(newax, neway);
9297
9298       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9299       {
9300         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9301         return;
9302       }
9303     }
9304   }
9305
9306   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9307       (neway == lev_fieldy - 1 && newax != ax))
9308   {
9309     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9310     Store[newax][neway] = element;
9311   }
9312   else if (neway == ay || element == EL_EMC_DRIPPER)
9313   {
9314     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9315
9316     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9317   }
9318   else
9319   {
9320     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9321     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9322     Store[ax][ay] = EL_AMOEBA_DROP;
9323     ContinueMoving(ax, ay);
9324     return;
9325   }
9326
9327   TEST_DrawLevelField(newax, neway);
9328 }
9329
9330 static void Life(int ax, int ay)
9331 {
9332   int x1, y1, x2, y2;
9333   int life_time = 40;
9334   int element = Tile[ax][ay];
9335   int graphic = el2img(element);
9336   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9337                          level.biomaze);
9338   boolean changed = FALSE;
9339
9340   if (IS_ANIMATED(graphic))
9341     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9342
9343   if (Stop[ax][ay])
9344     return;
9345
9346   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9347     MovDelay[ax][ay] = life_time;
9348
9349   if (MovDelay[ax][ay])         // wait some time before next cycle
9350   {
9351     MovDelay[ax][ay]--;
9352     if (MovDelay[ax][ay])
9353       return;
9354   }
9355
9356   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9357   {
9358     int xx = ax+x1, yy = ay+y1;
9359     int old_element = Tile[xx][yy];
9360     int num_neighbours = 0;
9361
9362     if (!IN_LEV_FIELD(xx, yy))
9363       continue;
9364
9365     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9366     {
9367       int x = xx+x2, y = yy+y2;
9368
9369       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9370         continue;
9371
9372       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9373       boolean is_neighbour = FALSE;
9374
9375       if (level.use_life_bugs)
9376         is_neighbour =
9377           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9378            (IS_FREE(x, y)                             &&  Stop[x][y]));
9379       else
9380         is_neighbour =
9381           (Last[x][y] == element || is_player_cell);
9382
9383       if (is_neighbour)
9384         num_neighbours++;
9385     }
9386
9387     boolean is_free = FALSE;
9388
9389     if (level.use_life_bugs)
9390       is_free = (IS_FREE(xx, yy));
9391     else
9392       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9393
9394     if (xx == ax && yy == ay)           // field in the middle
9395     {
9396       if (num_neighbours < life_parameter[0] ||
9397           num_neighbours > life_parameter[1])
9398       {
9399         Tile[xx][yy] = EL_EMPTY;
9400         if (Tile[xx][yy] != old_element)
9401           TEST_DrawLevelField(xx, yy);
9402         Stop[xx][yy] = TRUE;
9403         changed = TRUE;
9404       }
9405     }
9406     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9407     {                                   // free border field
9408       if (num_neighbours >= life_parameter[2] &&
9409           num_neighbours <= life_parameter[3])
9410       {
9411         Tile[xx][yy] = element;
9412         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9413         if (Tile[xx][yy] != old_element)
9414           TEST_DrawLevelField(xx, yy);
9415         Stop[xx][yy] = TRUE;
9416         changed = TRUE;
9417       }
9418     }
9419   }
9420
9421   if (changed)
9422     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9423                    SND_GAME_OF_LIFE_GROWING);
9424 }
9425
9426 static void InitRobotWheel(int x, int y)
9427 {
9428   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9429 }
9430
9431 static void RunRobotWheel(int x, int y)
9432 {
9433   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9434 }
9435
9436 static void StopRobotWheel(int x, int y)
9437 {
9438   if (game.robot_wheel_x == x &&
9439       game.robot_wheel_y == y)
9440   {
9441     game.robot_wheel_x = -1;
9442     game.robot_wheel_y = -1;
9443     game.robot_wheel_active = FALSE;
9444   }
9445 }
9446
9447 static void InitTimegateWheel(int x, int y)
9448 {
9449   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9450 }
9451
9452 static void RunTimegateWheel(int x, int y)
9453 {
9454   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9455 }
9456
9457 static void InitMagicBallDelay(int x, int y)
9458 {
9459   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9460 }
9461
9462 static void ActivateMagicBall(int bx, int by)
9463 {
9464   int x, y;
9465
9466   if (level.ball_random)
9467   {
9468     int pos_border = RND(8);    // select one of the eight border elements
9469     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9470     int xx = pos_content % 3;
9471     int yy = pos_content / 3;
9472
9473     x = bx - 1 + xx;
9474     y = by - 1 + yy;
9475
9476     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9477       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9478   }
9479   else
9480   {
9481     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9482     {
9483       int xx = x - bx + 1;
9484       int yy = y - by + 1;
9485
9486       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9487         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9488     }
9489   }
9490
9491   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9492 }
9493
9494 static void CheckExit(int x, int y)
9495 {
9496   if (game.gems_still_needed > 0 ||
9497       game.sokoban_fields_still_needed > 0 ||
9498       game.sokoban_objects_still_needed > 0 ||
9499       game.lights_still_needed > 0)
9500   {
9501     int element = Tile[x][y];
9502     int graphic = el2img(element);
9503
9504     if (IS_ANIMATED(graphic))
9505       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9506
9507     return;
9508   }
9509
9510   // do not re-open exit door closed after last player
9511   if (game.all_players_gone)
9512     return;
9513
9514   Tile[x][y] = EL_EXIT_OPENING;
9515
9516   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9517 }
9518
9519 static void CheckExitEM(int x, int y)
9520 {
9521   if (game.gems_still_needed > 0 ||
9522       game.sokoban_fields_still_needed > 0 ||
9523       game.sokoban_objects_still_needed > 0 ||
9524       game.lights_still_needed > 0)
9525   {
9526     int element = Tile[x][y];
9527     int graphic = el2img(element);
9528
9529     if (IS_ANIMATED(graphic))
9530       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9531
9532     return;
9533   }
9534
9535   // do not re-open exit door closed after last player
9536   if (game.all_players_gone)
9537     return;
9538
9539   Tile[x][y] = EL_EM_EXIT_OPENING;
9540
9541   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9542 }
9543
9544 static void CheckExitSteel(int x, int y)
9545 {
9546   if (game.gems_still_needed > 0 ||
9547       game.sokoban_fields_still_needed > 0 ||
9548       game.sokoban_objects_still_needed > 0 ||
9549       game.lights_still_needed > 0)
9550   {
9551     int element = Tile[x][y];
9552     int graphic = el2img(element);
9553
9554     if (IS_ANIMATED(graphic))
9555       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9556
9557     return;
9558   }
9559
9560   // do not re-open exit door closed after last player
9561   if (game.all_players_gone)
9562     return;
9563
9564   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9565
9566   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9567 }
9568
9569 static void CheckExitSteelEM(int x, int y)
9570 {
9571   if (game.gems_still_needed > 0 ||
9572       game.sokoban_fields_still_needed > 0 ||
9573       game.sokoban_objects_still_needed > 0 ||
9574       game.lights_still_needed > 0)
9575   {
9576     int element = Tile[x][y];
9577     int graphic = el2img(element);
9578
9579     if (IS_ANIMATED(graphic))
9580       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9581
9582     return;
9583   }
9584
9585   // do not re-open exit door closed after last player
9586   if (game.all_players_gone)
9587     return;
9588
9589   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9590
9591   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9592 }
9593
9594 static void CheckExitSP(int x, int y)
9595 {
9596   if (game.gems_still_needed > 0)
9597   {
9598     int element = Tile[x][y];
9599     int graphic = el2img(element);
9600
9601     if (IS_ANIMATED(graphic))
9602       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9603
9604     return;
9605   }
9606
9607   // do not re-open exit door closed after last player
9608   if (game.all_players_gone)
9609     return;
9610
9611   Tile[x][y] = EL_SP_EXIT_OPENING;
9612
9613   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9614 }
9615
9616 static void CloseAllOpenTimegates(void)
9617 {
9618   int x, y;
9619
9620   SCAN_PLAYFIELD(x, y)
9621   {
9622     int element = Tile[x][y];
9623
9624     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9625     {
9626       Tile[x][y] = EL_TIMEGATE_CLOSING;
9627
9628       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9629     }
9630   }
9631 }
9632
9633 static void DrawTwinkleOnField(int x, int y)
9634 {
9635   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9636     return;
9637
9638   if (Tile[x][y] == EL_BD_DIAMOND)
9639     return;
9640
9641   if (MovDelay[x][y] == 0)      // next animation frame
9642     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9643
9644   if (MovDelay[x][y] != 0)      // wait some time before next frame
9645   {
9646     MovDelay[x][y]--;
9647
9648     DrawLevelElementAnimation(x, y, Tile[x][y]);
9649
9650     if (MovDelay[x][y] != 0)
9651     {
9652       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9653                                            10 - MovDelay[x][y]);
9654
9655       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9656     }
9657   }
9658 }
9659
9660 static void MauerWaechst(int x, int y)
9661 {
9662   int delay = 6;
9663
9664   if (!MovDelay[x][y])          // next animation frame
9665     MovDelay[x][y] = 3 * delay;
9666
9667   if (MovDelay[x][y])           // wait some time before next frame
9668   {
9669     MovDelay[x][y]--;
9670
9671     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9672     {
9673       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9674       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9675
9676       DrawLevelGraphic(x, y, graphic, frame);
9677     }
9678
9679     if (!MovDelay[x][y])
9680     {
9681       if (MovDir[x][y] == MV_LEFT)
9682       {
9683         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9684           TEST_DrawLevelField(x - 1, y);
9685       }
9686       else if (MovDir[x][y] == MV_RIGHT)
9687       {
9688         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9689           TEST_DrawLevelField(x + 1, y);
9690       }
9691       else if (MovDir[x][y] == MV_UP)
9692       {
9693         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9694           TEST_DrawLevelField(x, y - 1);
9695       }
9696       else
9697       {
9698         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9699           TEST_DrawLevelField(x, y + 1);
9700       }
9701
9702       Tile[x][y] = Store[x][y];
9703       Store[x][y] = 0;
9704       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9705       TEST_DrawLevelField(x, y);
9706     }
9707   }
9708 }
9709
9710 static void MauerAbleger(int ax, int ay)
9711 {
9712   int element = Tile[ax][ay];
9713   int graphic = el2img(element);
9714   boolean oben_frei = FALSE, unten_frei = FALSE;
9715   boolean links_frei = FALSE, rechts_frei = FALSE;
9716   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9717   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9718   boolean new_wall = FALSE;
9719
9720   if (IS_ANIMATED(graphic))
9721     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9722
9723   if (!MovDelay[ax][ay])        // start building new wall
9724     MovDelay[ax][ay] = 6;
9725
9726   if (MovDelay[ax][ay])         // wait some time before building new wall
9727   {
9728     MovDelay[ax][ay]--;
9729     if (MovDelay[ax][ay])
9730       return;
9731   }
9732
9733   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9734     oben_frei = TRUE;
9735   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9736     unten_frei = TRUE;
9737   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9738     links_frei = TRUE;
9739   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9740     rechts_frei = TRUE;
9741
9742   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9743       element == EL_EXPANDABLE_WALL_ANY)
9744   {
9745     if (oben_frei)
9746     {
9747       Tile[ax][ay - 1] = EL_EXPANDABLE_WALL_GROWING;
9748       Store[ax][ay - 1] = element;
9749       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9750       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9751         DrawLevelGraphic(ax, ay - 1, IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9752       new_wall = TRUE;
9753     }
9754     if (unten_frei)
9755     {
9756       Tile[ax][ay + 1] = EL_EXPANDABLE_WALL_GROWING;
9757       Store[ax][ay + 1] = element;
9758       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9759       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9760         DrawLevelGraphic(ax, ay + 1, IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9761       new_wall = TRUE;
9762     }
9763   }
9764
9765   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9766       element == EL_EXPANDABLE_WALL_ANY ||
9767       element == EL_EXPANDABLE_WALL ||
9768       element == EL_BD_EXPANDABLE_WALL)
9769   {
9770     if (links_frei)
9771     {
9772       Tile[ax - 1][ay] = EL_EXPANDABLE_WALL_GROWING;
9773       Store[ax - 1][ay] = element;
9774       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9775       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9776         DrawLevelGraphic(ax - 1, ay, IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9777       new_wall = TRUE;
9778     }
9779
9780     if (rechts_frei)
9781     {
9782       Tile[ax + 1][ay] = EL_EXPANDABLE_WALL_GROWING;
9783       Store[ax + 1][ay] = element;
9784       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9785       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9786         DrawLevelGraphic(ax + 1, ay, IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9787       new_wall = TRUE;
9788     }
9789   }
9790
9791   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9792     TEST_DrawLevelField(ax, ay);
9793
9794   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9795     oben_massiv = TRUE;
9796   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9797     unten_massiv = TRUE;
9798   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9799     links_massiv = TRUE;
9800   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9801     rechts_massiv = TRUE;
9802
9803   if (((oben_massiv && unten_massiv) ||
9804        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9805        element == EL_EXPANDABLE_WALL) &&
9806       ((links_massiv && rechts_massiv) ||
9807        element == EL_EXPANDABLE_WALL_VERTICAL))
9808     Tile[ax][ay] = EL_WALL;
9809
9810   if (new_wall)
9811     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9812 }
9813
9814 static void MauerAblegerStahl(int ax, int ay)
9815 {
9816   int element = Tile[ax][ay];
9817   int graphic = el2img(element);
9818   boolean oben_frei = FALSE, unten_frei = FALSE;
9819   boolean links_frei = FALSE, rechts_frei = FALSE;
9820   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9821   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9822   boolean new_wall = FALSE;
9823
9824   if (IS_ANIMATED(graphic))
9825     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9826
9827   if (!MovDelay[ax][ay])        // start building new wall
9828     MovDelay[ax][ay] = 6;
9829
9830   if (MovDelay[ax][ay])         // wait some time before building new wall
9831   {
9832     MovDelay[ax][ay]--;
9833     if (MovDelay[ax][ay])
9834       return;
9835   }
9836
9837   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9838     oben_frei = TRUE;
9839   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9840     unten_frei = TRUE;
9841   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9842     links_frei = TRUE;
9843   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9844     rechts_frei = TRUE;
9845
9846   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9847       element == EL_EXPANDABLE_STEELWALL_ANY)
9848   {
9849     if (oben_frei)
9850     {
9851       Tile[ax][ay - 1] = EL_EXPANDABLE_STEELWALL_GROWING;
9852       Store[ax][ay - 1] = element;
9853       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9854       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9855         DrawLevelGraphic(ax, ay - 1, IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9856       new_wall = TRUE;
9857     }
9858     if (unten_frei)
9859     {
9860       Tile[ax][ay + 1] = EL_EXPANDABLE_STEELWALL_GROWING;
9861       Store[ax][ay + 1] = element;
9862       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9863       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9864         DrawLevelGraphic(ax, ay + 1, IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9865       new_wall = TRUE;
9866     }
9867   }
9868
9869   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9870       element == EL_EXPANDABLE_STEELWALL_ANY)
9871   {
9872     if (links_frei)
9873     {
9874       Tile[ax - 1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9875       Store[ax - 1][ay] = element;
9876       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9877       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9878         DrawLevelGraphic(ax - 1, ay, IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9879       new_wall = TRUE;
9880     }
9881
9882     if (rechts_frei)
9883     {
9884       Tile[ax + 1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9885       Store[ax + 1][ay] = element;
9886       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9887       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9888         DrawLevelGraphic(ax + 1, ay, IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9889       new_wall = TRUE;
9890     }
9891   }
9892
9893   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9894     oben_massiv = TRUE;
9895   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9896     unten_massiv = TRUE;
9897   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9898     links_massiv = TRUE;
9899   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9900     rechts_massiv = TRUE;
9901
9902   if (((oben_massiv && unten_massiv) ||
9903        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9904       ((links_massiv && rechts_massiv) ||
9905        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9906     Tile[ax][ay] = EL_STEELWALL;
9907
9908   if (new_wall)
9909     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9910 }
9911
9912 static void CheckForDragon(int x, int y)
9913 {
9914   int i, j;
9915   boolean dragon_found = FALSE;
9916   struct XY *xy = xy_topdown;
9917
9918   for (i = 0; i < NUM_DIRECTIONS; i++)
9919   {
9920     for (j = 0; j < 4; j++)
9921     {
9922       int xx = x + j * xy[i].x;
9923       int yy = y + j * xy[i].y;
9924
9925       if (IN_LEV_FIELD(xx, yy) &&
9926           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9927       {
9928         if (Tile[xx][yy] == EL_DRAGON)
9929           dragon_found = TRUE;
9930       }
9931       else
9932         break;
9933     }
9934   }
9935
9936   if (!dragon_found)
9937   {
9938     for (i = 0; i < NUM_DIRECTIONS; i++)
9939     {
9940       for (j = 0; j < 3; j++)
9941       {
9942         int xx = x + j * xy[i].x;
9943         int yy = y + j * xy[i].y;
9944
9945         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9946         {
9947           Tile[xx][yy] = EL_EMPTY;
9948           TEST_DrawLevelField(xx, yy);
9949         }
9950         else
9951           break;
9952       }
9953     }
9954   }
9955 }
9956
9957 static void InitBuggyBase(int x, int y)
9958 {
9959   int element = Tile[x][y];
9960   int activating_delay = FRAMES_PER_SECOND / 4;
9961
9962   ChangeDelay[x][y] =
9963     (element == EL_SP_BUGGY_BASE ?
9964      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9965      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9966      activating_delay :
9967      element == EL_SP_BUGGY_BASE_ACTIVE ?
9968      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9969 }
9970
9971 static void WarnBuggyBase(int x, int y)
9972 {
9973   int i;
9974   struct XY *xy = xy_topdown;
9975
9976   for (i = 0; i < NUM_DIRECTIONS; i++)
9977   {
9978     int xx = x + xy[i].x;
9979     int yy = y + xy[i].y;
9980
9981     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9982     {
9983       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9984
9985       break;
9986     }
9987   }
9988 }
9989
9990 static void InitTrap(int x, int y)
9991 {
9992   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9993 }
9994
9995 static void ActivateTrap(int x, int y)
9996 {
9997   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9998 }
9999
10000 static void ChangeActiveTrap(int x, int y)
10001 {
10002   int graphic = IMG_TRAP_ACTIVE;
10003
10004   // if new animation frame was drawn, correct crumbled sand border
10005   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10006     TEST_DrawLevelFieldCrumbled(x, y);
10007 }
10008
10009 static int getSpecialActionElement(int element, int number, int base_element)
10010 {
10011   return (element != EL_EMPTY ? element :
10012           number != -1 ? base_element + number - 1 :
10013           EL_EMPTY);
10014 }
10015
10016 static int getModifiedActionNumber(int value_old, int operator, int operand,
10017                                    int value_min, int value_max)
10018 {
10019   int value_new = (operator == CA_MODE_SET      ? operand :
10020                    operator == CA_MODE_ADD      ? value_old + operand :
10021                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10022                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10023                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10024                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10025                    value_old);
10026
10027   return (value_new < value_min ? value_min :
10028           value_new > value_max ? value_max :
10029           value_new);
10030 }
10031
10032 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10033 {
10034   struct ElementInfo *ei = &element_info[element];
10035   struct ElementChangeInfo *change = &ei->change_page[page];
10036   int target_element = change->target_element;
10037   int action_type = change->action_type;
10038   int action_mode = change->action_mode;
10039   int action_arg = change->action_arg;
10040   int action_element = change->action_element;
10041   int i;
10042
10043   if (!change->has_action)
10044     return;
10045
10046   // ---------- determine action paramater values -----------------------------
10047
10048   int level_time_value =
10049     (level.time > 0 ? TimeLeft :
10050      TimePlayed);
10051
10052   int action_arg_element_raw =
10053     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10054      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10055      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10056      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10057      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10058      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10059      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10060      EL_EMPTY);
10061   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10062
10063   int action_arg_direction =
10064     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10065      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10066      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10067      change->actual_trigger_side :
10068      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10069      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10070      MV_NONE);
10071
10072   int action_arg_number_min =
10073     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10074      CA_ARG_MIN);
10075
10076   int action_arg_number_max =
10077     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10078      action_type == CA_SET_LEVEL_GEMS ? 999 :
10079      action_type == CA_SET_LEVEL_TIME ? 9999 :
10080      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10081      action_type == CA_SET_CE_VALUE ? 9999 :
10082      action_type == CA_SET_CE_SCORE ? 9999 :
10083      CA_ARG_MAX);
10084
10085   int action_arg_number_reset =
10086     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10087      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10088      action_type == CA_SET_LEVEL_TIME ? level.time :
10089      action_type == CA_SET_LEVEL_SCORE ? 0 :
10090      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10091      action_type == CA_SET_CE_SCORE ? 0 :
10092      0);
10093
10094   int action_arg_number =
10095     (action_arg <= CA_ARG_MAX ? action_arg :
10096      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10097      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10098      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10099      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10100      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10101      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10102      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10103      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10104      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10105      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10106      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10107      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10108      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10109      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10110      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10111      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10112      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10113      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10114      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10115      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10116      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10117      -1);
10118
10119   int action_arg_number_old =
10120     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10121      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10122      action_type == CA_SET_LEVEL_SCORE ? game.score :
10123      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10124      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10125      0);
10126
10127   int action_arg_number_new =
10128     getModifiedActionNumber(action_arg_number_old,
10129                             action_mode, action_arg_number,
10130                             action_arg_number_min, action_arg_number_max);
10131
10132   int trigger_player_bits =
10133     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10134      change->actual_trigger_player_bits : change->trigger_player);
10135
10136   int action_arg_player_bits =
10137     (action_arg >= CA_ARG_PLAYER_1 &&
10138      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10139      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10140      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10141      PLAYER_BITS_ANY);
10142
10143   // ---------- execute action  -----------------------------------------------
10144
10145   switch (action_type)
10146   {
10147     case CA_NO_ACTION:
10148     {
10149       return;
10150     }
10151
10152     // ---------- level actions  ----------------------------------------------
10153
10154     case CA_RESTART_LEVEL:
10155     {
10156       game.restart_level = TRUE;
10157
10158       break;
10159     }
10160
10161     case CA_SHOW_ENVELOPE:
10162     {
10163       int element = getSpecialActionElement(action_arg_element,
10164                                             action_arg_number, EL_ENVELOPE_1);
10165
10166       if (IS_ENVELOPE(element))
10167         local_player->show_envelope = element;
10168
10169       break;
10170     }
10171
10172     case CA_SET_LEVEL_TIME:
10173     {
10174       if (level.time > 0)       // only modify limited time value
10175       {
10176         TimeLeft = action_arg_number_new;
10177
10178         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10179
10180         DisplayGameControlValues();
10181
10182         if (!TimeLeft && game.time_limit)
10183           for (i = 0; i < MAX_PLAYERS; i++)
10184             KillPlayer(&stored_player[i]);
10185       }
10186
10187       break;
10188     }
10189
10190     case CA_SET_LEVEL_SCORE:
10191     {
10192       game.score = action_arg_number_new;
10193
10194       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10195
10196       DisplayGameControlValues();
10197
10198       break;
10199     }
10200
10201     case CA_SET_LEVEL_GEMS:
10202     {
10203       game.gems_still_needed = action_arg_number_new;
10204
10205       game.snapshot.collected_item = TRUE;
10206
10207       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10208
10209       DisplayGameControlValues();
10210
10211       break;
10212     }
10213
10214     case CA_SET_LEVEL_WIND:
10215     {
10216       game.wind_direction = action_arg_direction;
10217
10218       break;
10219     }
10220
10221     case CA_SET_LEVEL_RANDOM_SEED:
10222     {
10223       // ensure that setting a new random seed while playing is predictable
10224       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10225
10226       break;
10227     }
10228
10229     // ---------- player actions  ---------------------------------------------
10230
10231     case CA_MOVE_PLAYER:
10232     case CA_MOVE_PLAYER_NEW:
10233     {
10234       // automatically move to the next field in specified direction
10235       for (i = 0; i < MAX_PLAYERS; i++)
10236         if (trigger_player_bits & (1 << i))
10237           if (action_type == CA_MOVE_PLAYER ||
10238               stored_player[i].MovPos == 0)
10239             stored_player[i].programmed_action = action_arg_direction;
10240
10241       break;
10242     }
10243
10244     case CA_EXIT_PLAYER:
10245     {
10246       for (i = 0; i < MAX_PLAYERS; i++)
10247         if (action_arg_player_bits & (1 << i))
10248           ExitPlayer(&stored_player[i]);
10249
10250       if (game.players_still_needed == 0)
10251         LevelSolved();
10252
10253       break;
10254     }
10255
10256     case CA_KILL_PLAYER:
10257     {
10258       for (i = 0; i < MAX_PLAYERS; i++)
10259         if (action_arg_player_bits & (1 << i))
10260           KillPlayer(&stored_player[i]);
10261
10262       break;
10263     }
10264
10265     case CA_SET_PLAYER_KEYS:
10266     {
10267       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10268       int element = getSpecialActionElement(action_arg_element,
10269                                             action_arg_number, EL_KEY_1);
10270
10271       if (IS_KEY(element))
10272       {
10273         for (i = 0; i < MAX_PLAYERS; i++)
10274         {
10275           if (trigger_player_bits & (1 << i))
10276           {
10277             stored_player[i].key[KEY_NR(element)] = key_state;
10278
10279             DrawGameDoorValues();
10280           }
10281         }
10282       }
10283
10284       break;
10285     }
10286
10287     case CA_SET_PLAYER_SPEED:
10288     {
10289       for (i = 0; i < MAX_PLAYERS; i++)
10290       {
10291         if (trigger_player_bits & (1 << i))
10292         {
10293           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10294
10295           if (action_arg == CA_ARG_SPEED_FASTER &&
10296               stored_player[i].cannot_move)
10297           {
10298             action_arg_number = STEPSIZE_VERY_SLOW;
10299           }
10300           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10301                    action_arg == CA_ARG_SPEED_FASTER)
10302           {
10303             action_arg_number = 2;
10304             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10305                            CA_MODE_MULTIPLY);
10306           }
10307           else if (action_arg == CA_ARG_NUMBER_RESET)
10308           {
10309             action_arg_number = level.initial_player_stepsize[i];
10310           }
10311
10312           move_stepsize =
10313             getModifiedActionNumber(move_stepsize,
10314                                     action_mode,
10315                                     action_arg_number,
10316                                     action_arg_number_min,
10317                                     action_arg_number_max);
10318
10319           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10320         }
10321       }
10322
10323       break;
10324     }
10325
10326     case CA_SET_PLAYER_SHIELD:
10327     {
10328       for (i = 0; i < MAX_PLAYERS; i++)
10329       {
10330         if (trigger_player_bits & (1 << i))
10331         {
10332           if (action_arg == CA_ARG_SHIELD_OFF)
10333           {
10334             stored_player[i].shield_normal_time_left = 0;
10335             stored_player[i].shield_deadly_time_left = 0;
10336           }
10337           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10338           {
10339             stored_player[i].shield_normal_time_left = 999999;
10340           }
10341           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10342           {
10343             stored_player[i].shield_normal_time_left = 999999;
10344             stored_player[i].shield_deadly_time_left = 999999;
10345           }
10346         }
10347       }
10348
10349       break;
10350     }
10351
10352     case CA_SET_PLAYER_GRAVITY:
10353     {
10354       for (i = 0; i < MAX_PLAYERS; i++)
10355       {
10356         if (trigger_player_bits & (1 << i))
10357         {
10358           stored_player[i].gravity =
10359             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10360              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10361              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10362              stored_player[i].gravity);
10363         }
10364       }
10365
10366       break;
10367     }
10368
10369     case CA_SET_PLAYER_ARTWORK:
10370     {
10371       for (i = 0; i < MAX_PLAYERS; i++)
10372       {
10373         if (trigger_player_bits & (1 << i))
10374         {
10375           int artwork_element = action_arg_element;
10376
10377           if (action_arg == CA_ARG_ELEMENT_RESET)
10378             artwork_element =
10379               (level.use_artwork_element[i] ? level.artwork_element[i] :
10380                stored_player[i].element_nr);
10381
10382           if (stored_player[i].artwork_element != artwork_element)
10383             stored_player[i].Frame = 0;
10384
10385           stored_player[i].artwork_element = artwork_element;
10386
10387           SetPlayerWaiting(&stored_player[i], FALSE);
10388
10389           // set number of special actions for bored and sleeping animation
10390           stored_player[i].num_special_action_bored =
10391             get_num_special_action(artwork_element,
10392                                    ACTION_BORING_1, ACTION_BORING_LAST);
10393           stored_player[i].num_special_action_sleeping =
10394             get_num_special_action(artwork_element,
10395                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10396         }
10397       }
10398
10399       break;
10400     }
10401
10402     case CA_SET_PLAYER_INVENTORY:
10403     {
10404       for (i = 0; i < MAX_PLAYERS; i++)
10405       {
10406         struct PlayerInfo *player = &stored_player[i];
10407         int j, k;
10408
10409         if (trigger_player_bits & (1 << i))
10410         {
10411           int inventory_element = action_arg_element;
10412
10413           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10414               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10415               action_arg == CA_ARG_ELEMENT_ACTION)
10416           {
10417             int element = inventory_element;
10418             int collect_count = element_info[element].collect_count_initial;
10419
10420             if (!IS_CUSTOM_ELEMENT(element))
10421               collect_count = 1;
10422
10423             if (collect_count == 0)
10424               player->inventory_infinite_element = element;
10425             else
10426               for (k = 0; k < collect_count; k++)
10427                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10428                   player->inventory_element[player->inventory_size++] =
10429                     element;
10430           }
10431           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10432                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10433                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10434           {
10435             if (player->inventory_infinite_element != EL_UNDEFINED &&
10436                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10437                                      action_arg_element_raw))
10438               player->inventory_infinite_element = EL_UNDEFINED;
10439
10440             for (k = 0, j = 0; j < player->inventory_size; j++)
10441             {
10442               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10443                                         action_arg_element_raw))
10444                 player->inventory_element[k++] = player->inventory_element[j];
10445             }
10446
10447             player->inventory_size = k;
10448           }
10449           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10450           {
10451             if (player->inventory_size > 0)
10452             {
10453               for (j = 0; j < player->inventory_size - 1; j++)
10454                 player->inventory_element[j] = player->inventory_element[j + 1];
10455
10456               player->inventory_size--;
10457             }
10458           }
10459           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10460           {
10461             if (player->inventory_size > 0)
10462               player->inventory_size--;
10463           }
10464           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10465           {
10466             player->inventory_infinite_element = EL_UNDEFINED;
10467             player->inventory_size = 0;
10468           }
10469           else if (action_arg == CA_ARG_INVENTORY_RESET)
10470           {
10471             player->inventory_infinite_element = EL_UNDEFINED;
10472             player->inventory_size = 0;
10473
10474             if (level.use_initial_inventory[i])
10475             {
10476               for (j = 0; j < level.initial_inventory_size[i]; j++)
10477               {
10478                 int element = level.initial_inventory_content[i][j];
10479                 int collect_count = element_info[element].collect_count_initial;
10480
10481                 if (!IS_CUSTOM_ELEMENT(element))
10482                   collect_count = 1;
10483
10484                 if (collect_count == 0)
10485                   player->inventory_infinite_element = element;
10486                 else
10487                   for (k = 0; k < collect_count; k++)
10488                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10489                       player->inventory_element[player->inventory_size++] =
10490                         element;
10491               }
10492             }
10493           }
10494         }
10495       }
10496
10497       break;
10498     }
10499
10500     // ---------- CE actions  -------------------------------------------------
10501
10502     case CA_SET_CE_VALUE:
10503     {
10504       int last_ce_value = CustomValue[x][y];
10505
10506       CustomValue[x][y] = action_arg_number_new;
10507
10508       if (CustomValue[x][y] != last_ce_value)
10509       {
10510         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10511         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10512
10513         if (CustomValue[x][y] == 0)
10514         {
10515           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10516           ChangeCount[x][y] = 0;        // allow at least one more change
10517
10518           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10519           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10520         }
10521       }
10522
10523       break;
10524     }
10525
10526     case CA_SET_CE_SCORE:
10527     {
10528       int last_ce_score = ei->collect_score;
10529
10530       ei->collect_score = action_arg_number_new;
10531
10532       if (ei->collect_score != last_ce_score)
10533       {
10534         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10535         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10536
10537         if (ei->collect_score == 0)
10538         {
10539           int xx, yy;
10540
10541           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10542           ChangeCount[x][y] = 0;        // allow at least one more change
10543
10544           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10545           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10546
10547           /*
10548             This is a very special case that seems to be a mixture between
10549             CheckElementChange() and CheckTriggeredElementChange(): while
10550             the first one only affects single elements that are triggered
10551             directly, the second one affects multiple elements in the playfield
10552             that are triggered indirectly by another element. This is a third
10553             case: Changing the CE score always affects multiple identical CEs,
10554             so every affected CE must be checked, not only the single CE for
10555             which the CE score was changed in the first place (as every instance
10556             of that CE shares the same CE score, and therefore also can change)!
10557           */
10558           SCAN_PLAYFIELD(xx, yy)
10559           {
10560             if (Tile[xx][yy] == element)
10561               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10562                                  CE_SCORE_GETS_ZERO);
10563           }
10564         }
10565       }
10566
10567       break;
10568     }
10569
10570     case CA_SET_CE_ARTWORK:
10571     {
10572       int artwork_element = action_arg_element;
10573       boolean reset_frame = FALSE;
10574       int xx, yy;
10575
10576       if (action_arg == CA_ARG_ELEMENT_RESET)
10577         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10578                            element);
10579
10580       if (ei->gfx_element != artwork_element)
10581         reset_frame = TRUE;
10582
10583       ei->gfx_element = artwork_element;
10584
10585       SCAN_PLAYFIELD(xx, yy)
10586       {
10587         if (Tile[xx][yy] == element)
10588         {
10589           if (reset_frame)
10590           {
10591             ResetGfxAnimation(xx, yy);
10592             ResetRandomAnimationValue(xx, yy);
10593           }
10594
10595           TEST_DrawLevelField(xx, yy);
10596         }
10597       }
10598
10599       break;
10600     }
10601
10602     // ---------- engine actions  ---------------------------------------------
10603
10604     case CA_SET_ENGINE_SCAN_MODE:
10605     {
10606       InitPlayfieldScanMode(action_arg);
10607
10608       break;
10609     }
10610
10611     default:
10612       break;
10613   }
10614 }
10615
10616 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10617 {
10618   int old_element = Tile[x][y];
10619   int new_element = GetElementFromGroupElement(element);
10620   int previous_move_direction = MovDir[x][y];
10621   int last_ce_value = CustomValue[x][y];
10622   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10623   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10624   boolean add_player_onto_element = (new_element_is_player &&
10625                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10626                                      IS_WALKABLE(old_element));
10627
10628   if (!add_player_onto_element)
10629   {
10630     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10631       RemoveMovingField(x, y);
10632     else
10633       RemoveField(x, y);
10634
10635     Tile[x][y] = new_element;
10636
10637     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10638       MovDir[x][y] = previous_move_direction;
10639
10640     if (element_info[new_element].use_last_ce_value)
10641       CustomValue[x][y] = last_ce_value;
10642
10643     InitField_WithBug1(x, y, FALSE);
10644
10645     new_element = Tile[x][y];   // element may have changed
10646
10647     ResetGfxAnimation(x, y);
10648     ResetRandomAnimationValue(x, y);
10649
10650     TEST_DrawLevelField(x, y);
10651
10652     if (GFX_CRUMBLED(new_element))
10653       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10654   }
10655
10656   // check if element under the player changes from accessible to unaccessible
10657   // (needed for special case of dropping element which then changes)
10658   // (must be checked after creating new element for walkable group elements)
10659   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10660       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10661   {
10662     Bang(x, y);
10663
10664     return;
10665   }
10666
10667   // "ChangeCount" not set yet to allow "entered by player" change one time
10668   if (new_element_is_player)
10669     RelocatePlayer(x, y, new_element);
10670
10671   if (is_change)
10672     ChangeCount[x][y]++;        // count number of changes in the same frame
10673
10674   TestIfBadThingTouchesPlayer(x, y);
10675   TestIfPlayerTouchesCustomElement(x, y);
10676   TestIfElementTouchesCustomElement(x, y);
10677 }
10678
10679 static void CreateField(int x, int y, int element)
10680 {
10681   CreateFieldExt(x, y, element, FALSE);
10682 }
10683
10684 static void CreateElementFromChange(int x, int y, int element)
10685 {
10686   element = GET_VALID_RUNTIME_ELEMENT(element);
10687
10688   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10689   {
10690     int old_element = Tile[x][y];
10691
10692     // prevent changed element from moving in same engine frame
10693     // unless both old and new element can either fall or move
10694     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10695         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10696       Stop[x][y] = TRUE;
10697   }
10698
10699   CreateFieldExt(x, y, element, TRUE);
10700 }
10701
10702 static boolean ChangeElement(int x, int y, int element, int page)
10703 {
10704   struct ElementInfo *ei = &element_info[element];
10705   struct ElementChangeInfo *change = &ei->change_page[page];
10706   int ce_value = CustomValue[x][y];
10707   int ce_score = ei->collect_score;
10708   int target_element;
10709   int old_element = Tile[x][y];
10710
10711   // always use default change event to prevent running into a loop
10712   if (ChangeEvent[x][y] == -1)
10713     ChangeEvent[x][y] = CE_DELAY;
10714
10715   if (ChangeEvent[x][y] == CE_DELAY)
10716   {
10717     // reset actual trigger element, trigger player and action element
10718     change->actual_trigger_element = EL_EMPTY;
10719     change->actual_trigger_player = EL_EMPTY;
10720     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10721     change->actual_trigger_side = CH_SIDE_NONE;
10722     change->actual_trigger_ce_value = 0;
10723     change->actual_trigger_ce_score = 0;
10724   }
10725
10726   // do not change elements more than a specified maximum number of changes
10727   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10728     return FALSE;
10729
10730   ChangeCount[x][y]++;          // count number of changes in the same frame
10731
10732   if (change->explode)
10733   {
10734     Bang(x, y);
10735
10736     return TRUE;
10737   }
10738
10739   if (change->use_target_content)
10740   {
10741     boolean complete_replace = TRUE;
10742     boolean can_replace[3][3];
10743     int xx, yy;
10744
10745     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10746     {
10747       boolean is_empty;
10748       boolean is_walkable;
10749       boolean is_diggable;
10750       boolean is_collectible;
10751       boolean is_removable;
10752       boolean is_destructible;
10753       int ex = x + xx - 1;
10754       int ey = y + yy - 1;
10755       int content_element = change->target_content.e[xx][yy];
10756       int e;
10757
10758       can_replace[xx][yy] = TRUE;
10759
10760       if (ex == x && ey == y)   // do not check changing element itself
10761         continue;
10762
10763       if (content_element == EL_EMPTY_SPACE)
10764       {
10765         can_replace[xx][yy] = FALSE;    // do not replace border with space
10766
10767         continue;
10768       }
10769
10770       if (!IN_LEV_FIELD(ex, ey))
10771       {
10772         can_replace[xx][yy] = FALSE;
10773         complete_replace = FALSE;
10774
10775         continue;
10776       }
10777
10778       e = Tile[ex][ey];
10779
10780       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10781         e = MovingOrBlocked2Element(ex, ey);
10782
10783       is_empty = (IS_FREE(ex, ey) ||
10784                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10785
10786       is_walkable     = (is_empty || IS_WALKABLE(e));
10787       is_diggable     = (is_empty || IS_DIGGABLE(e));
10788       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10789       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10790       is_removable    = (is_diggable || is_collectible);
10791
10792       can_replace[xx][yy] =
10793         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10794           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10795           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10796           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10797           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10798           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10799          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10800
10801       if (!can_replace[xx][yy])
10802         complete_replace = FALSE;
10803     }
10804
10805     if (!change->only_if_complete || complete_replace)
10806     {
10807       boolean something_has_changed = FALSE;
10808
10809       if (change->only_if_complete && change->use_random_replace &&
10810           RND(100) < change->random_percentage)
10811         return FALSE;
10812
10813       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10814       {
10815         int ex = x + xx - 1;
10816         int ey = y + yy - 1;
10817         int content_element;
10818
10819         if (can_replace[xx][yy] && (!change->use_random_replace ||
10820                                     RND(100) < change->random_percentage))
10821         {
10822           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10823             RemoveMovingField(ex, ey);
10824
10825           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10826
10827           content_element = change->target_content.e[xx][yy];
10828           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10829                                               ce_value, ce_score);
10830
10831           CreateElementFromChange(ex, ey, target_element);
10832
10833           something_has_changed = TRUE;
10834
10835           // for symmetry reasons, freeze newly created border elements
10836           if (ex != x || ey != y)
10837             Stop[ex][ey] = TRUE;        // no more moving in this frame
10838         }
10839       }
10840
10841       if (something_has_changed)
10842       {
10843         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10844         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10845       }
10846     }
10847   }
10848   else
10849   {
10850     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10851                                         ce_value, ce_score);
10852
10853     if (element == EL_DIAGONAL_GROWING ||
10854         element == EL_DIAGONAL_SHRINKING)
10855     {
10856       target_element = Store[x][y];
10857
10858       Store[x][y] = EL_EMPTY;
10859     }
10860
10861     // special case: element changes to player (and may be kept if walkable)
10862     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10863       CreateElementFromChange(x, y, EL_EMPTY);
10864
10865     CreateElementFromChange(x, y, target_element);
10866
10867     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10868     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10869   }
10870
10871   // this uses direct change before indirect change
10872   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10873
10874   return TRUE;
10875 }
10876
10877 static void HandleElementChange(int x, int y, int page)
10878 {
10879   int element = MovingOrBlocked2Element(x, y);
10880   struct ElementInfo *ei = &element_info[element];
10881   struct ElementChangeInfo *change = &ei->change_page[page];
10882   boolean handle_action_before_change = FALSE;
10883
10884 #ifdef DEBUG
10885   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10886       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10887   {
10888     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10889           x, y, element, element_info[element].token_name);
10890     Debug("game:playing:HandleElementChange", "This should never happen!");
10891   }
10892 #endif
10893
10894   // this can happen with classic bombs on walkable, changing elements
10895   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10896   {
10897     return;
10898   }
10899
10900   if (ChangeDelay[x][y] == 0)           // initialize element change
10901   {
10902     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10903
10904     if (change->can_change)
10905     {
10906       // !!! not clear why graphic animation should be reset at all here !!!
10907       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10908       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10909
10910       /*
10911         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10912
10913         When using an animation frame delay of 1 (this only happens with
10914         "sp_zonk.moving.left/right" in the classic graphics), the default
10915         (non-moving) animation shows wrong animation frames (while the
10916         moving animation, like "sp_zonk.moving.left/right", is correct,
10917         so this graphical bug never shows up with the classic graphics).
10918         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10919         be drawn instead of the correct frames 0,1,2,3. This is caused by
10920         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10921         an element change: First when the change delay ("ChangeDelay[][]")
10922         counter has reached zero after decrementing, then a second time in
10923         the next frame (after "GfxFrame[][]" was already incremented) when
10924         "ChangeDelay[][]" is reset to the initial delay value again.
10925
10926         This causes frame 0 to be drawn twice, while the last frame won't
10927         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10928
10929         As some animations may already be cleverly designed around this bug
10930         (at least the "Snake Bite" snake tail animation does this), it cannot
10931         simply be fixed here without breaking such existing animations.
10932         Unfortunately, it cannot easily be detected if a graphics set was
10933         designed "before" or "after" the bug was fixed. As a workaround,
10934         a new graphics set option "game.graphics_engine_version" was added
10935         to be able to specify the game's major release version for which the
10936         graphics set was designed, which can then be used to decide if the
10937         bugfix should be used (version 4 and above) or not (version 3 or
10938         below, or if no version was specified at all, as with old sets).
10939
10940         (The wrong/fixed animation frames can be tested with the test level set
10941         "test_gfxframe" and level "000", which contains a specially prepared
10942         custom element at level position (x/y) == (11/9) which uses the zonk
10943         animation mentioned above. Using "game.graphics_engine_version: 4"
10944         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10945         This can also be seen from the debug output for this test element.)
10946       */
10947
10948       // when a custom element is about to change (for example by change delay),
10949       // do not reset graphic animation when the custom element is moving
10950       if (game.graphics_engine_version < 4 &&
10951           !IS_MOVING(x, y))
10952       {
10953         ResetGfxAnimation(x, y);
10954         ResetRandomAnimationValue(x, y);
10955       }
10956
10957       if (change->pre_change_function)
10958         change->pre_change_function(x, y);
10959     }
10960   }
10961
10962   ChangeDelay[x][y]--;
10963
10964   if (ChangeDelay[x][y] != 0)           // continue element change
10965   {
10966     if (change->can_change)
10967     {
10968       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10969
10970       if (IS_ANIMATED(graphic))
10971         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10972
10973       if (change->change_function)
10974         change->change_function(x, y);
10975     }
10976   }
10977   else                                  // finish element change
10978   {
10979     if (ChangePage[x][y] != -1)         // remember page from delayed change
10980     {
10981       page = ChangePage[x][y];
10982       ChangePage[x][y] = -1;
10983
10984       change = &ei->change_page[page];
10985     }
10986
10987     if (IS_MOVING(x, y))                // never change a running system ;-)
10988     {
10989       ChangeDelay[x][y] = 1;            // try change after next move step
10990       ChangePage[x][y] = page;          // remember page to use for change
10991
10992       return;
10993     }
10994
10995     // special case: set new level random seed before changing element
10996     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10997       handle_action_before_change = TRUE;
10998
10999     if (change->has_action && handle_action_before_change)
11000       ExecuteCustomElementAction(x, y, element, page);
11001
11002     if (change->can_change)
11003     {
11004       if (ChangeElement(x, y, element, page))
11005       {
11006         if (change->post_change_function)
11007           change->post_change_function(x, y);
11008       }
11009     }
11010
11011     if (change->has_action && !handle_action_before_change)
11012       ExecuteCustomElementAction(x, y, element, page);
11013   }
11014 }
11015
11016 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11017                                               int trigger_element,
11018                                               int trigger_event,
11019                                               int trigger_player,
11020                                               int trigger_side,
11021                                               int trigger_page)
11022 {
11023   boolean change_done_any = FALSE;
11024   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11025   int i;
11026
11027   if (!(trigger_events[trigger_element][trigger_event]))
11028     return FALSE;
11029
11030   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11031
11032   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11033   {
11034     int element = EL_CUSTOM_START + i;
11035     boolean change_done = FALSE;
11036     int p;
11037
11038     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11039         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11040       continue;
11041
11042     for (p = 0; p < element_info[element].num_change_pages; p++)
11043     {
11044       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11045
11046       if (change->can_change_or_has_action &&
11047           change->has_event[trigger_event] &&
11048           change->trigger_side & trigger_side &&
11049           change->trigger_player & trigger_player &&
11050           change->trigger_page & trigger_page_bits &&
11051           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11052       {
11053         change->actual_trigger_element = trigger_element;
11054         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11055         change->actual_trigger_player_bits = trigger_player;
11056         change->actual_trigger_side = trigger_side;
11057         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11058         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11059
11060         if ((change->can_change && !change_done) || change->has_action)
11061         {
11062           int x, y;
11063
11064           SCAN_PLAYFIELD(x, y)
11065           {
11066             if (Tile[x][y] == element)
11067             {
11068               if (change->can_change && !change_done)
11069               {
11070                 // if element already changed in this frame, not only prevent
11071                 // another element change (checked in ChangeElement()), but
11072                 // also prevent additional element actions for this element
11073
11074                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11075                     !level.use_action_after_change_bug)
11076                   continue;
11077
11078                 ChangeDelay[x][y] = 1;
11079                 ChangeEvent[x][y] = trigger_event;
11080
11081                 HandleElementChange(x, y, p);
11082               }
11083               else if (change->has_action)
11084               {
11085                 // if element already changed in this frame, not only prevent
11086                 // another element change (checked in ChangeElement()), but
11087                 // also prevent additional element actions for this element
11088
11089                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11090                     !level.use_action_after_change_bug)
11091                   continue;
11092
11093                 ExecuteCustomElementAction(x, y, element, p);
11094                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11095               }
11096             }
11097           }
11098
11099           if (change->can_change)
11100           {
11101             change_done = TRUE;
11102             change_done_any = TRUE;
11103           }
11104         }
11105       }
11106     }
11107   }
11108
11109   RECURSION_LOOP_DETECTION_END();
11110
11111   return change_done_any;
11112 }
11113
11114 static boolean CheckElementChangeExt(int x, int y,
11115                                      int element,
11116                                      int trigger_element,
11117                                      int trigger_event,
11118                                      int trigger_player,
11119                                      int trigger_side)
11120 {
11121   boolean change_done = FALSE;
11122   int p;
11123
11124   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11125       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11126     return FALSE;
11127
11128   if (Tile[x][y] == EL_BLOCKED)
11129   {
11130     Blocked2Moving(x, y, &x, &y);
11131     element = Tile[x][y];
11132   }
11133
11134   // check if element has already changed or is about to change after moving
11135   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11136        Tile[x][y] != element) ||
11137
11138       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11139        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11140         ChangePage[x][y] != -1)))
11141     return FALSE;
11142
11143   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11144
11145   for (p = 0; p < element_info[element].num_change_pages; p++)
11146   {
11147     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11148
11149     /* check trigger element for all events where the element that is checked
11150        for changing interacts with a directly adjacent element -- this is
11151        different to element changes that affect other elements to change on the
11152        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11153     boolean check_trigger_element =
11154       (trigger_event == CE_NEXT_TO_X ||
11155        trigger_event == CE_TOUCHING_X ||
11156        trigger_event == CE_HITTING_X ||
11157        trigger_event == CE_HIT_BY_X ||
11158        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11159
11160     if (change->can_change_or_has_action &&
11161         change->has_event[trigger_event] &&
11162         change->trigger_side & trigger_side &&
11163         change->trigger_player & trigger_player &&
11164         (!check_trigger_element ||
11165          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11166     {
11167       change->actual_trigger_element = trigger_element;
11168       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11169       change->actual_trigger_player_bits = trigger_player;
11170       change->actual_trigger_side = trigger_side;
11171       change->actual_trigger_ce_value = CustomValue[x][y];
11172       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11173
11174       // special case: trigger element not at (x,y) position for some events
11175       if (check_trigger_element)
11176       {
11177         static struct
11178         {
11179           int dx, dy;
11180         } move_xy[] =
11181           {
11182             {  0,  0 },
11183             { -1,  0 },
11184             { +1,  0 },
11185             {  0,  0 },
11186             {  0, -1 },
11187             {  0,  0 }, { 0, 0 }, { 0, 0 },
11188             {  0, +1 }
11189           };
11190
11191         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11192         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11193
11194         change->actual_trigger_ce_value = CustomValue[xx][yy];
11195         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11196       }
11197
11198       if (change->can_change && !change_done)
11199       {
11200         ChangeDelay[x][y] = 1;
11201         ChangeEvent[x][y] = trigger_event;
11202
11203         HandleElementChange(x, y, p);
11204
11205         change_done = TRUE;
11206       }
11207       else if (change->has_action)
11208       {
11209         ExecuteCustomElementAction(x, y, element, p);
11210         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11211       }
11212     }
11213   }
11214
11215   RECURSION_LOOP_DETECTION_END();
11216
11217   return change_done;
11218 }
11219
11220 static void PlayPlayerSound(struct PlayerInfo *player)
11221 {
11222   int jx = player->jx, jy = player->jy;
11223   int sound_element = player->artwork_element;
11224   int last_action = player->last_action_waiting;
11225   int action = player->action_waiting;
11226
11227   if (player->is_waiting)
11228   {
11229     if (action != last_action)
11230       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11231     else
11232       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11233   }
11234   else
11235   {
11236     if (action != last_action)
11237       StopSound(element_info[sound_element].sound[last_action]);
11238
11239     if (last_action == ACTION_SLEEPING)
11240       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11241   }
11242 }
11243
11244 static void PlayAllPlayersSound(void)
11245 {
11246   int i;
11247
11248   for (i = 0; i < MAX_PLAYERS; i++)
11249     if (stored_player[i].active)
11250       PlayPlayerSound(&stored_player[i]);
11251 }
11252
11253 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11254 {
11255   boolean last_waiting = player->is_waiting;
11256   int move_dir = player->MovDir;
11257
11258   player->dir_waiting = move_dir;
11259   player->last_action_waiting = player->action_waiting;
11260
11261   if (is_waiting)
11262   {
11263     if (!last_waiting)          // not waiting -> waiting
11264     {
11265       player->is_waiting = TRUE;
11266
11267       player->frame_counter_bored =
11268         FrameCounter +
11269         game.player_boring_delay_fixed +
11270         GetSimpleRandom(game.player_boring_delay_random);
11271       player->frame_counter_sleeping =
11272         FrameCounter +
11273         game.player_sleeping_delay_fixed +
11274         GetSimpleRandom(game.player_sleeping_delay_random);
11275
11276       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11277     }
11278
11279     if (game.player_sleeping_delay_fixed +
11280         game.player_sleeping_delay_random > 0 &&
11281         player->anim_delay_counter == 0 &&
11282         player->post_delay_counter == 0 &&
11283         FrameCounter >= player->frame_counter_sleeping)
11284       player->is_sleeping = TRUE;
11285     else if (game.player_boring_delay_fixed +
11286              game.player_boring_delay_random > 0 &&
11287              FrameCounter >= player->frame_counter_bored)
11288       player->is_bored = TRUE;
11289
11290     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11291                               player->is_bored ? ACTION_BORING :
11292                               ACTION_WAITING);
11293
11294     if (player->is_sleeping && player->use_murphy)
11295     {
11296       // special case for sleeping Murphy when leaning against non-free tile
11297
11298       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11299           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11300            !IS_MOVING(player->jx - 1, player->jy)))
11301         move_dir = MV_LEFT;
11302       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11303                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11304                 !IS_MOVING(player->jx + 1, player->jy)))
11305         move_dir = MV_RIGHT;
11306       else
11307         player->is_sleeping = FALSE;
11308
11309       player->dir_waiting = move_dir;
11310     }
11311
11312     if (player->is_sleeping)
11313     {
11314       if (player->num_special_action_sleeping > 0)
11315       {
11316         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11317         {
11318           int last_special_action = player->special_action_sleeping;
11319           int num_special_action = player->num_special_action_sleeping;
11320           int special_action =
11321             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11322              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11323              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11324              last_special_action + 1 : ACTION_SLEEPING);
11325           int special_graphic =
11326             el_act_dir2img(player->artwork_element, special_action, move_dir);
11327
11328           player->anim_delay_counter =
11329             graphic_info[special_graphic].anim_delay_fixed +
11330             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11331           player->post_delay_counter =
11332             graphic_info[special_graphic].post_delay_fixed +
11333             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11334
11335           player->special_action_sleeping = special_action;
11336         }
11337
11338         if (player->anim_delay_counter > 0)
11339         {
11340           player->action_waiting = player->special_action_sleeping;
11341           player->anim_delay_counter--;
11342         }
11343         else if (player->post_delay_counter > 0)
11344         {
11345           player->post_delay_counter--;
11346         }
11347       }
11348     }
11349     else if (player->is_bored)
11350     {
11351       if (player->num_special_action_bored > 0)
11352       {
11353         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11354         {
11355           int special_action =
11356             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11357           int special_graphic =
11358             el_act_dir2img(player->artwork_element, special_action, move_dir);
11359
11360           player->anim_delay_counter =
11361             graphic_info[special_graphic].anim_delay_fixed +
11362             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11363           player->post_delay_counter =
11364             graphic_info[special_graphic].post_delay_fixed +
11365             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11366
11367           player->special_action_bored = special_action;
11368         }
11369
11370         if (player->anim_delay_counter > 0)
11371         {
11372           player->action_waiting = player->special_action_bored;
11373           player->anim_delay_counter--;
11374         }
11375         else if (player->post_delay_counter > 0)
11376         {
11377           player->post_delay_counter--;
11378         }
11379       }
11380     }
11381   }
11382   else if (last_waiting)        // waiting -> not waiting
11383   {
11384     player->is_waiting = FALSE;
11385     player->is_bored = FALSE;
11386     player->is_sleeping = FALSE;
11387
11388     player->frame_counter_bored = -1;
11389     player->frame_counter_sleeping = -1;
11390
11391     player->anim_delay_counter = 0;
11392     player->post_delay_counter = 0;
11393
11394     player->dir_waiting = player->MovDir;
11395     player->action_waiting = ACTION_DEFAULT;
11396
11397     player->special_action_bored = ACTION_DEFAULT;
11398     player->special_action_sleeping = ACTION_DEFAULT;
11399   }
11400 }
11401
11402 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11403 {
11404   if ((!player->is_moving  && player->was_moving) ||
11405       (player->MovPos == 0 && player->was_moving) ||
11406       (player->is_snapping && !player->was_snapping) ||
11407       (player->is_dropping && !player->was_dropping))
11408   {
11409     if (!CheckSaveEngineSnapshotToList())
11410       return;
11411
11412     player->was_moving = FALSE;
11413     player->was_snapping = TRUE;
11414     player->was_dropping = TRUE;
11415   }
11416   else
11417   {
11418     if (player->is_moving)
11419       player->was_moving = TRUE;
11420
11421     if (!player->is_snapping)
11422       player->was_snapping = FALSE;
11423
11424     if (!player->is_dropping)
11425       player->was_dropping = FALSE;
11426   }
11427
11428   static struct MouseActionInfo mouse_action_last = { 0 };
11429   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11430   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11431
11432   if (new_released)
11433     CheckSaveEngineSnapshotToList();
11434
11435   mouse_action_last = mouse_action;
11436 }
11437
11438 static void CheckSingleStepMode(struct PlayerInfo *player)
11439 {
11440   if (tape.single_step && tape.recording && !tape.pausing)
11441   {
11442     // as it is called "single step mode", just return to pause mode when the
11443     // player stopped moving after one tile (or never starts moving at all)
11444     // (reverse logic needed here in case single step mode used in team mode)
11445     if (player->is_moving ||
11446         player->is_pushing ||
11447         player->is_dropping_pressed ||
11448         player->effective_mouse_action.button)
11449       game.enter_single_step_mode = FALSE;
11450   }
11451
11452   CheckSaveEngineSnapshot(player);
11453 }
11454
11455 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11456 {
11457   int left      = player_action & JOY_LEFT;
11458   int right     = player_action & JOY_RIGHT;
11459   int up        = player_action & JOY_UP;
11460   int down      = player_action & JOY_DOWN;
11461   int button1   = player_action & JOY_BUTTON_1;
11462   int button2   = player_action & JOY_BUTTON_2;
11463   int dx        = (left ? -1 : right ? 1 : 0);
11464   int dy        = (up   ? -1 : down  ? 1 : 0);
11465
11466   if (!player->active || tape.pausing)
11467     return 0;
11468
11469   if (player_action)
11470   {
11471     if (button1)
11472       SnapField(player, dx, dy);
11473     else
11474     {
11475       if (button2)
11476         DropElement(player);
11477
11478       MovePlayer(player, dx, dy);
11479     }
11480
11481     CheckSingleStepMode(player);
11482
11483     SetPlayerWaiting(player, FALSE);
11484
11485     return player_action;
11486   }
11487   else
11488   {
11489     // no actions for this player (no input at player's configured device)
11490
11491     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11492     SnapField(player, 0, 0);
11493     CheckGravityMovementWhenNotMoving(player);
11494
11495     if (player->MovPos == 0)
11496       SetPlayerWaiting(player, TRUE);
11497
11498     if (player->MovPos == 0)    // needed for tape.playing
11499       player->is_moving = FALSE;
11500
11501     player->is_dropping = FALSE;
11502     player->is_dropping_pressed = FALSE;
11503     player->drop_pressed_delay = 0;
11504
11505     CheckSingleStepMode(player);
11506
11507     return 0;
11508   }
11509 }
11510
11511 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11512                                          byte *tape_action)
11513 {
11514   if (!tape.use_mouse_actions)
11515     return;
11516
11517   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11518   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11519   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11520 }
11521
11522 static void SetTapeActionFromMouseAction(byte *tape_action,
11523                                          struct MouseActionInfo *mouse_action)
11524 {
11525   if (!tape.use_mouse_actions)
11526     return;
11527
11528   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11529   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11530   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11531 }
11532
11533 static void CheckLevelSolved(void)
11534 {
11535   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11536   {
11537     if (game_em.level_solved &&
11538         !game_em.game_over)                             // game won
11539     {
11540       LevelSolved();
11541
11542       game_em.game_over = TRUE;
11543
11544       game.all_players_gone = TRUE;
11545     }
11546
11547     if (game_em.game_over)                              // game lost
11548       game.all_players_gone = TRUE;
11549   }
11550   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11551   {
11552     if (game_sp.level_solved &&
11553         !game_sp.game_over)                             // game won
11554     {
11555       LevelSolved();
11556
11557       game_sp.game_over = TRUE;
11558
11559       game.all_players_gone = TRUE;
11560     }
11561
11562     if (game_sp.game_over)                              // game lost
11563       game.all_players_gone = TRUE;
11564   }
11565   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11566   {
11567     if (game_mm.level_solved &&
11568         !game_mm.game_over)                             // game won
11569     {
11570       LevelSolved();
11571
11572       game_mm.game_over = TRUE;
11573
11574       game.all_players_gone = TRUE;
11575     }
11576
11577     if (game_mm.game_over)                              // game lost
11578       game.all_players_gone = TRUE;
11579   }
11580 }
11581
11582 static void CheckLevelTime_StepCounter(void)
11583 {
11584   int i;
11585
11586   TimePlayed++;
11587
11588   if (TimeLeft > 0)
11589   {
11590     TimeLeft--;
11591
11592     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11593       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11594
11595     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11596
11597     DisplayGameControlValues();
11598
11599     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11600       for (i = 0; i < MAX_PLAYERS; i++)
11601         KillPlayer(&stored_player[i]);
11602   }
11603   else if (game.no_level_time_limit && !game.all_players_gone)
11604   {
11605     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11606
11607     DisplayGameControlValues();
11608   }
11609 }
11610
11611 static void CheckLevelTime(void)
11612 {
11613   int i;
11614
11615   if (TimeFrames >= FRAMES_PER_SECOND)
11616   {
11617     TimeFrames = 0;
11618     TapeTime++;
11619
11620     for (i = 0; i < MAX_PLAYERS; i++)
11621     {
11622       struct PlayerInfo *player = &stored_player[i];
11623
11624       if (SHIELD_ON(player))
11625       {
11626         player->shield_normal_time_left--;
11627
11628         if (player->shield_deadly_time_left > 0)
11629           player->shield_deadly_time_left--;
11630       }
11631     }
11632
11633     if (!game.LevelSolved && !level.use_step_counter)
11634     {
11635       TimePlayed++;
11636
11637       if (TimeLeft > 0)
11638       {
11639         TimeLeft--;
11640
11641         if (TimeLeft <= 10 && game.time_limit)
11642           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11643
11644         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11645            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11646
11647         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11648
11649         if (!TimeLeft && game.time_limit)
11650         {
11651           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11652             game_em.lev->killed_out_of_time = TRUE;
11653           else
11654             for (i = 0; i < MAX_PLAYERS; i++)
11655               KillPlayer(&stored_player[i]);
11656         }
11657       }
11658       else if (game.no_level_time_limit && !game.all_players_gone)
11659       {
11660         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11661       }
11662
11663       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11664     }
11665
11666     if (tape.recording || tape.playing)
11667       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11668   }
11669
11670   if (tape.recording || tape.playing)
11671     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11672
11673   UpdateAndDisplayGameControlValues();
11674 }
11675
11676 void AdvanceFrameAndPlayerCounters(int player_nr)
11677 {
11678   int i;
11679
11680   // advance frame counters (global frame counter and time frame counter)
11681   FrameCounter++;
11682   TimeFrames++;
11683
11684   // advance player counters (counters for move delay, move animation etc.)
11685   for (i = 0; i < MAX_PLAYERS; i++)
11686   {
11687     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11688     int move_delay_value = stored_player[i].move_delay_value;
11689     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11690
11691     if (!advance_player_counters)       // not all players may be affected
11692       continue;
11693
11694     if (move_frames == 0)       // less than one move per game frame
11695     {
11696       int stepsize = TILEX / move_delay_value;
11697       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11698       int count = (stored_player[i].is_moving ?
11699                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11700
11701       if (count % delay == 0)
11702         move_frames = 1;
11703     }
11704
11705     stored_player[i].Frame += move_frames;
11706
11707     if (stored_player[i].MovPos != 0)
11708       stored_player[i].StepFrame += move_frames;
11709
11710     if (stored_player[i].move_delay > 0)
11711       stored_player[i].move_delay--;
11712
11713     // due to bugs in previous versions, counter must count up, not down
11714     if (stored_player[i].push_delay != -1)
11715       stored_player[i].push_delay++;
11716
11717     if (stored_player[i].drop_delay > 0)
11718       stored_player[i].drop_delay--;
11719
11720     if (stored_player[i].is_dropping_pressed)
11721       stored_player[i].drop_pressed_delay++;
11722   }
11723 }
11724
11725 void StartGameActions(boolean init_network_game, boolean record_tape,
11726                       int random_seed)
11727 {
11728   unsigned int new_random_seed = InitRND(random_seed);
11729
11730   if (record_tape)
11731     TapeStartRecording(new_random_seed);
11732
11733   if (setup.auto_pause_on_start && !tape.pausing)
11734     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11735
11736   if (init_network_game)
11737   {
11738     SendToServer_LevelFile();
11739     SendToServer_StartPlaying();
11740
11741     return;
11742   }
11743
11744   InitGame();
11745 }
11746
11747 static void GameActionsExt(void)
11748 {
11749 #if 0
11750   static unsigned int game_frame_delay = 0;
11751 #endif
11752   unsigned int game_frame_delay_value;
11753   byte *recorded_player_action;
11754   byte summarized_player_action = 0;
11755   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11756   int i;
11757
11758   // detect endless loops, caused by custom element programming
11759   if (recursion_loop_detected && recursion_loop_depth == 0)
11760   {
11761     char *message = getStringCat3("Internal Error! Element ",
11762                                   EL_NAME(recursion_loop_element),
11763                                   " caused endless loop! Quit the game?");
11764
11765     Warn("element '%s' caused endless loop in game engine",
11766          EL_NAME(recursion_loop_element));
11767
11768     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11769
11770     recursion_loop_detected = FALSE;    // if game should be continued
11771
11772     free(message);
11773
11774     return;
11775   }
11776
11777   if (game.restart_level)
11778     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11779
11780   CheckLevelSolved();
11781
11782   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11783     GameWon();
11784
11785   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11786     TapeStop();
11787
11788   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11789     return;
11790
11791   game_frame_delay_value =
11792     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11793
11794   if (tape.playing && tape.warp_forward && !tape.pausing)
11795     game_frame_delay_value = 0;
11796
11797   SetVideoFrameDelay(game_frame_delay_value);
11798
11799   // (de)activate virtual buttons depending on current game status
11800   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11801   {
11802     if (game.all_players_gone)  // if no players there to be controlled anymore
11803       SetOverlayActive(FALSE);
11804     else if (!tape.playing)     // if game continues after tape stopped playing
11805       SetOverlayActive(TRUE);
11806   }
11807
11808 #if 0
11809 #if 0
11810   // ---------- main game synchronization point ----------
11811
11812   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11813
11814   Debug("game:playing:skip", "skip == %d", skip);
11815
11816 #else
11817   // ---------- main game synchronization point ----------
11818
11819   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11820 #endif
11821 #endif
11822
11823   if (network_playing && !network_player_action_received)
11824   {
11825     // try to get network player actions in time
11826
11827     // last chance to get network player actions without main loop delay
11828     HandleNetworking();
11829
11830     // game was quit by network peer
11831     if (game_status != GAME_MODE_PLAYING)
11832       return;
11833
11834     // check if network player actions still missing and game still running
11835     if (!network_player_action_received && !checkGameEnded())
11836       return;           // failed to get network player actions in time
11837
11838     // do not yet reset "network_player_action_received" (for tape.pausing)
11839   }
11840
11841   if (tape.pausing)
11842     return;
11843
11844   // at this point we know that we really continue executing the game
11845
11846   network_player_action_received = FALSE;
11847
11848   // when playing tape, read previously recorded player input from tape data
11849   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11850
11851   local_player->effective_mouse_action = local_player->mouse_action;
11852
11853   if (recorded_player_action != NULL)
11854     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11855                                  recorded_player_action);
11856
11857   // TapePlayAction() may return NULL when toggling to "pause before death"
11858   if (tape.pausing)
11859     return;
11860
11861   if (tape.set_centered_player)
11862   {
11863     game.centered_player_nr_next = tape.centered_player_nr_next;
11864     game.set_centered_player = TRUE;
11865   }
11866
11867   for (i = 0; i < MAX_PLAYERS; i++)
11868   {
11869     summarized_player_action |= stored_player[i].action;
11870
11871     if (!network_playing && (game.team_mode || tape.playing))
11872       stored_player[i].effective_action = stored_player[i].action;
11873   }
11874
11875   if (network_playing && !checkGameEnded())
11876     SendToServer_MovePlayer(summarized_player_action);
11877
11878   // summarize all actions at local players mapped input device position
11879   // (this allows using different input devices in single player mode)
11880   if (!network.enabled && !game.team_mode)
11881     stored_player[map_player_action[local_player->index_nr]].effective_action =
11882       summarized_player_action;
11883
11884   // summarize all actions at centered player in local team mode
11885   if (tape.recording &&
11886       setup.team_mode && !network.enabled &&
11887       setup.input_on_focus &&
11888       game.centered_player_nr != -1)
11889   {
11890     for (i = 0; i < MAX_PLAYERS; i++)
11891       stored_player[map_player_action[i]].effective_action =
11892         (i == game.centered_player_nr ? summarized_player_action : 0);
11893   }
11894
11895   if (recorded_player_action != NULL)
11896     for (i = 0; i < MAX_PLAYERS; i++)
11897       stored_player[i].effective_action = recorded_player_action[i];
11898
11899   for (i = 0; i < MAX_PLAYERS; i++)
11900   {
11901     tape_action[i] = stored_player[i].effective_action;
11902
11903     /* (this may happen in the RND game engine if a player was not present on
11904        the playfield on level start, but appeared later from a custom element */
11905     if (setup.team_mode &&
11906         tape.recording &&
11907         tape_action[i] &&
11908         !tape.player_participates[i])
11909       tape.player_participates[i] = TRUE;
11910   }
11911
11912   SetTapeActionFromMouseAction(tape_action,
11913                                &local_player->effective_mouse_action);
11914
11915   // only record actions from input devices, but not programmed actions
11916   if (tape.recording)
11917     TapeRecordAction(tape_action);
11918
11919   // remember if game was played (especially after tape stopped playing)
11920   if (!tape.playing && summarized_player_action)
11921     game.GamePlayed = TRUE;
11922
11923 #if USE_NEW_PLAYER_ASSIGNMENTS
11924   // !!! also map player actions in single player mode !!!
11925   // if (game.team_mode)
11926   if (1)
11927   {
11928     byte mapped_action[MAX_PLAYERS];
11929
11930 #if DEBUG_PLAYER_ACTIONS
11931     for (i = 0; i < MAX_PLAYERS; i++)
11932       DebugContinued("", "%d, ", stored_player[i].effective_action);
11933 #endif
11934
11935     for (i = 0; i < MAX_PLAYERS; i++)
11936       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11937
11938     for (i = 0; i < MAX_PLAYERS; i++)
11939       stored_player[i].effective_action = mapped_action[i];
11940
11941 #if DEBUG_PLAYER_ACTIONS
11942     DebugContinued("", "=> ");
11943     for (i = 0; i < MAX_PLAYERS; i++)
11944       DebugContinued("", "%d, ", stored_player[i].effective_action);
11945     DebugContinued("game:playing:player", "\n");
11946 #endif
11947   }
11948 #if DEBUG_PLAYER_ACTIONS
11949   else
11950   {
11951     for (i = 0; i < MAX_PLAYERS; i++)
11952       DebugContinued("", "%d, ", stored_player[i].effective_action);
11953     DebugContinued("game:playing:player", "\n");
11954   }
11955 #endif
11956 #endif
11957
11958   for (i = 0; i < MAX_PLAYERS; i++)
11959   {
11960     // allow engine snapshot in case of changed movement attempt
11961     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11962         (stored_player[i].effective_action & KEY_MOTION))
11963       game.snapshot.changed_action = TRUE;
11964
11965     // allow engine snapshot in case of snapping/dropping attempt
11966     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11967         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11968       game.snapshot.changed_action = TRUE;
11969
11970     game.snapshot.last_action[i] = stored_player[i].effective_action;
11971   }
11972
11973   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11974   {
11975     GameActions_EM_Main();
11976   }
11977   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11978   {
11979     GameActions_SP_Main();
11980   }
11981   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11982   {
11983     GameActions_MM_Main();
11984   }
11985   else
11986   {
11987     GameActions_RND_Main();
11988   }
11989
11990   BlitScreenToBitmap(backbuffer);
11991
11992   CheckLevelSolved();
11993   CheckLevelTime();
11994
11995   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11996
11997   if (global.show_frames_per_second)
11998   {
11999     static unsigned int fps_counter = 0;
12000     static int fps_frames = 0;
12001     unsigned int fps_delay_ms = Counter() - fps_counter;
12002
12003     fps_frames++;
12004
12005     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12006     {
12007       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12008
12009       fps_frames = 0;
12010       fps_counter = Counter();
12011
12012       // always draw FPS to screen after FPS value was updated
12013       redraw_mask |= REDRAW_FPS;
12014     }
12015
12016     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12017     if (GetDrawDeactivationMask() == REDRAW_NONE)
12018       redraw_mask |= REDRAW_FPS;
12019   }
12020 }
12021
12022 static void GameActions_CheckSaveEngineSnapshot(void)
12023 {
12024   if (!game.snapshot.save_snapshot)
12025     return;
12026
12027   // clear flag for saving snapshot _before_ saving snapshot
12028   game.snapshot.save_snapshot = FALSE;
12029
12030   SaveEngineSnapshotToList();
12031 }
12032
12033 void GameActions(void)
12034 {
12035   GameActionsExt();
12036
12037   GameActions_CheckSaveEngineSnapshot();
12038 }
12039
12040 void GameActions_EM_Main(void)
12041 {
12042   byte effective_action[MAX_PLAYERS];
12043   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12044   int i;
12045
12046   for (i = 0; i < MAX_PLAYERS; i++)
12047     effective_action[i] = stored_player[i].effective_action;
12048
12049   GameActions_EM(effective_action, warp_mode);
12050 }
12051
12052 void GameActions_SP_Main(void)
12053 {
12054   byte effective_action[MAX_PLAYERS];
12055   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12056   int i;
12057
12058   for (i = 0; i < MAX_PLAYERS; i++)
12059     effective_action[i] = stored_player[i].effective_action;
12060
12061   GameActions_SP(effective_action, warp_mode);
12062
12063   for (i = 0; i < MAX_PLAYERS; i++)
12064   {
12065     if (stored_player[i].force_dropping)
12066       stored_player[i].action |= KEY_BUTTON_DROP;
12067
12068     stored_player[i].force_dropping = FALSE;
12069   }
12070 }
12071
12072 void GameActions_MM_Main(void)
12073 {
12074   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12075
12076   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12077 }
12078
12079 void GameActions_RND_Main(void)
12080 {
12081   GameActions_RND();
12082 }
12083
12084 void GameActions_RND(void)
12085 {
12086   static struct MouseActionInfo mouse_action_last = { 0 };
12087   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12088   int magic_wall_x = 0, magic_wall_y = 0;
12089   int i, x, y, element, graphic, last_gfx_frame;
12090
12091   InitPlayfieldScanModeVars();
12092
12093   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12094   {
12095     SCAN_PLAYFIELD(x, y)
12096     {
12097       ChangeCount[x][y] = 0;
12098       ChangeEvent[x][y] = -1;
12099     }
12100   }
12101
12102   if (game.set_centered_player)
12103   {
12104     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12105
12106     // switching to "all players" only possible if all players fit to screen
12107     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12108     {
12109       game.centered_player_nr_next = game.centered_player_nr;
12110       game.set_centered_player = FALSE;
12111     }
12112
12113     // do not switch focus to non-existing (or non-active) player
12114     if (game.centered_player_nr_next >= 0 &&
12115         !stored_player[game.centered_player_nr_next].active)
12116     {
12117       game.centered_player_nr_next = game.centered_player_nr;
12118       game.set_centered_player = FALSE;
12119     }
12120   }
12121
12122   if (game.set_centered_player &&
12123       ScreenMovPos == 0)        // screen currently aligned at tile position
12124   {
12125     int sx, sy;
12126
12127     if (game.centered_player_nr_next == -1)
12128     {
12129       setScreenCenteredToAllPlayers(&sx, &sy);
12130     }
12131     else
12132     {
12133       sx = stored_player[game.centered_player_nr_next].jx;
12134       sy = stored_player[game.centered_player_nr_next].jy;
12135     }
12136
12137     game.centered_player_nr = game.centered_player_nr_next;
12138     game.set_centered_player = FALSE;
12139
12140     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12141     DrawGameDoorValues();
12142   }
12143
12144   // check single step mode (set flag and clear again if any player is active)
12145   game.enter_single_step_mode =
12146     (tape.single_step && tape.recording && !tape.pausing);
12147
12148   for (i = 0; i < MAX_PLAYERS; i++)
12149   {
12150     int actual_player_action = stored_player[i].effective_action;
12151
12152 #if 1
12153     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12154        - rnd_equinox_tetrachloride 048
12155        - rnd_equinox_tetrachloride_ii 096
12156        - rnd_emanuel_schmieg 002
12157        - doctor_sloan_ww 001, 020
12158     */
12159     if (stored_player[i].MovPos == 0)
12160       CheckGravityMovement(&stored_player[i]);
12161 #endif
12162
12163     // overwrite programmed action with tape action
12164     if (stored_player[i].programmed_action)
12165       actual_player_action = stored_player[i].programmed_action;
12166
12167     PlayerActions(&stored_player[i], actual_player_action);
12168
12169     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12170   }
12171
12172   // single step pause mode may already have been toggled by "ScrollPlayer()"
12173   if (game.enter_single_step_mode && !tape.pausing)
12174     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12175
12176   ScrollScreen(NULL, SCROLL_GO_ON);
12177
12178   /* for backwards compatibility, the following code emulates a fixed bug that
12179      occured when pushing elements (causing elements that just made their last
12180      pushing step to already (if possible) make their first falling step in the
12181      same game frame, which is bad); this code is also needed to use the famous
12182      "spring push bug" which is used in older levels and might be wanted to be
12183      used also in newer levels, but in this case the buggy pushing code is only
12184      affecting the "spring" element and no other elements */
12185
12186   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12187   {
12188     for (i = 0; i < MAX_PLAYERS; i++)
12189     {
12190       struct PlayerInfo *player = &stored_player[i];
12191       int x = player->jx;
12192       int y = player->jy;
12193
12194       if (player->active && player->is_pushing && player->is_moving &&
12195           IS_MOVING(x, y) &&
12196           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12197            Tile[x][y] == EL_SPRING))
12198       {
12199         ContinueMoving(x, y);
12200
12201         // continue moving after pushing (this is actually a bug)
12202         if (!IS_MOVING(x, y))
12203           Stop[x][y] = FALSE;
12204       }
12205     }
12206   }
12207
12208   SCAN_PLAYFIELD(x, y)
12209   {
12210     Last[x][y] = Tile[x][y];
12211
12212     ChangeCount[x][y] = 0;
12213     ChangeEvent[x][y] = -1;
12214
12215     // this must be handled before main playfield loop
12216     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12217     {
12218       MovDelay[x][y]--;
12219       if (MovDelay[x][y] <= 0)
12220         RemoveField(x, y);
12221     }
12222
12223     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12224     {
12225       MovDelay[x][y]--;
12226       if (MovDelay[x][y] <= 0)
12227       {
12228         int element = Store[x][y];
12229         int move_direction = MovDir[x][y];
12230         int player_index_bit = Store2[x][y];
12231
12232         Store[x][y] = 0;
12233         Store2[x][y] = 0;
12234
12235         RemoveField(x, y);
12236         TEST_DrawLevelField(x, y);
12237
12238         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12239
12240         if (IS_ENVELOPE(element))
12241           local_player->show_envelope = element;
12242       }
12243     }
12244
12245 #if DEBUG
12246     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12247     {
12248       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12249             x, y);
12250       Debug("game:playing:GameActions_RND", "This should never happen!");
12251
12252       ChangePage[x][y] = -1;
12253     }
12254 #endif
12255
12256     Stop[x][y] = FALSE;
12257     if (WasJustMoving[x][y] > 0)
12258       WasJustMoving[x][y]--;
12259     if (WasJustFalling[x][y] > 0)
12260       WasJustFalling[x][y]--;
12261     if (CheckCollision[x][y] > 0)
12262       CheckCollision[x][y]--;
12263     if (CheckImpact[x][y] > 0)
12264       CheckImpact[x][y]--;
12265
12266     GfxFrame[x][y]++;
12267
12268     /* reset finished pushing action (not done in ContinueMoving() to allow
12269        continuous pushing animation for elements with zero push delay) */
12270     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12271     {
12272       ResetGfxAnimation(x, y);
12273       TEST_DrawLevelField(x, y);
12274     }
12275
12276 #if DEBUG
12277     if (IS_BLOCKED(x, y))
12278     {
12279       int oldx, oldy;
12280
12281       Blocked2Moving(x, y, &oldx, &oldy);
12282       if (!IS_MOVING(oldx, oldy))
12283       {
12284         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12285         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12286         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12287         Debug("game:playing:GameActions_RND", "This should never happen!");
12288       }
12289     }
12290 #endif
12291   }
12292
12293   if (mouse_action.button)
12294   {
12295     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12296     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12297
12298     x = mouse_action.lx;
12299     y = mouse_action.ly;
12300     element = Tile[x][y];
12301
12302     if (new_button)
12303     {
12304       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12305       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12306                                          ch_button);
12307     }
12308
12309     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12310     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12311                                        ch_button);
12312
12313     if (level.use_step_counter)
12314     {
12315       boolean counted_click = FALSE;
12316
12317       // element clicked that can change when clicked/pressed
12318       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12319           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12320            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12321         counted_click = TRUE;
12322
12323       // element clicked that can trigger change when clicked/pressed
12324       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12325           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12326         counted_click = TRUE;
12327
12328       if (new_button && counted_click)
12329         CheckLevelTime_StepCounter();
12330     }
12331   }
12332
12333   SCAN_PLAYFIELD(x, y)
12334   {
12335     element = Tile[x][y];
12336     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12337     last_gfx_frame = GfxFrame[x][y];
12338
12339     if (element == EL_EMPTY)
12340       graphic = el2img(GfxElementEmpty[x][y]);
12341
12342     ResetGfxFrame(x, y);
12343
12344     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12345       DrawLevelGraphicAnimation(x, y, graphic);
12346
12347     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12348         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12349       ResetRandomAnimationValue(x, y);
12350
12351     SetRandomAnimationValue(x, y);
12352
12353     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12354
12355     if (IS_INACTIVE(element))
12356     {
12357       if (IS_ANIMATED(graphic))
12358         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12359
12360       continue;
12361     }
12362
12363     // this may take place after moving, so 'element' may have changed
12364     if (IS_CHANGING(x, y) &&
12365         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12366     {
12367       int page = element_info[element].event_page_nr[CE_DELAY];
12368
12369       HandleElementChange(x, y, page);
12370
12371       element = Tile[x][y];
12372       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12373     }
12374
12375     CheckNextToConditions(x, y);
12376
12377     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12378     {
12379       StartMoving(x, y);
12380
12381       element = Tile[x][y];
12382       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12383
12384       if (IS_ANIMATED(graphic) &&
12385           !IS_MOVING(x, y) &&
12386           !Stop[x][y])
12387         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12388
12389       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12390         TEST_DrawTwinkleOnField(x, y);
12391     }
12392     else if (element == EL_ACID)
12393     {
12394       if (!Stop[x][y])
12395         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12396     }
12397     else if ((element == EL_EXIT_OPEN ||
12398               element == EL_EM_EXIT_OPEN ||
12399               element == EL_SP_EXIT_OPEN ||
12400               element == EL_STEEL_EXIT_OPEN ||
12401               element == EL_EM_STEEL_EXIT_OPEN ||
12402               element == EL_SP_TERMINAL ||
12403               element == EL_SP_TERMINAL_ACTIVE ||
12404               element == EL_EXTRA_TIME ||
12405               element == EL_SHIELD_NORMAL ||
12406               element == EL_SHIELD_DEADLY) &&
12407              IS_ANIMATED(graphic))
12408       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12409     else if (IS_MOVING(x, y))
12410       ContinueMoving(x, y);
12411     else if (IS_ACTIVE_BOMB(element))
12412       CheckDynamite(x, y);
12413     else if (element == EL_AMOEBA_GROWING)
12414       AmoebaGrowing(x, y);
12415     else if (element == EL_AMOEBA_SHRINKING)
12416       AmoebaShrinking(x, y);
12417
12418 #if !USE_NEW_AMOEBA_CODE
12419     else if (IS_AMOEBALIVE(element))
12420       AmoebaReproduce(x, y);
12421 #endif
12422
12423     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12424       Life(x, y);
12425     else if (element == EL_EXIT_CLOSED)
12426       CheckExit(x, y);
12427     else if (element == EL_EM_EXIT_CLOSED)
12428       CheckExitEM(x, y);
12429     else if (element == EL_STEEL_EXIT_CLOSED)
12430       CheckExitSteel(x, y);
12431     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12432       CheckExitSteelEM(x, y);
12433     else if (element == EL_SP_EXIT_CLOSED)
12434       CheckExitSP(x, y);
12435     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12436              element == EL_EXPANDABLE_STEELWALL_GROWING)
12437       MauerWaechst(x, y);
12438     else if (element == EL_EXPANDABLE_WALL ||
12439              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12440              element == EL_EXPANDABLE_WALL_VERTICAL ||
12441              element == EL_EXPANDABLE_WALL_ANY ||
12442              element == EL_BD_EXPANDABLE_WALL)
12443       MauerAbleger(x, y);
12444     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12445              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12446              element == EL_EXPANDABLE_STEELWALL_ANY)
12447       MauerAblegerStahl(x, y);
12448     else if (element == EL_FLAMES)
12449       CheckForDragon(x, y);
12450     else if (element == EL_EXPLOSION)
12451       ; // drawing of correct explosion animation is handled separately
12452     else if (element == EL_ELEMENT_SNAPPING ||
12453              element == EL_DIAGONAL_SHRINKING ||
12454              element == EL_DIAGONAL_GROWING)
12455     {
12456       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12457
12458       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12459     }
12460     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12461       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12462
12463     if (IS_BELT_ACTIVE(element))
12464       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12465
12466     if (game.magic_wall_active)
12467     {
12468       int jx = local_player->jx, jy = local_player->jy;
12469
12470       // play the element sound at the position nearest to the player
12471       if ((element == EL_MAGIC_WALL_FULL ||
12472            element == EL_MAGIC_WALL_ACTIVE ||
12473            element == EL_MAGIC_WALL_EMPTYING ||
12474            element == EL_BD_MAGIC_WALL_FULL ||
12475            element == EL_BD_MAGIC_WALL_ACTIVE ||
12476            element == EL_BD_MAGIC_WALL_EMPTYING ||
12477            element == EL_DC_MAGIC_WALL_FULL ||
12478            element == EL_DC_MAGIC_WALL_ACTIVE ||
12479            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12480           ABS(x - jx) + ABS(y - jy) <
12481           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12482       {
12483         magic_wall_x = x;
12484         magic_wall_y = y;
12485       }
12486     }
12487   }
12488
12489 #if USE_NEW_AMOEBA_CODE
12490   // new experimental amoeba growth stuff
12491   if (!(FrameCounter % 8))
12492   {
12493     static unsigned int random = 1684108901;
12494
12495     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12496     {
12497       x = RND(lev_fieldx);
12498       y = RND(lev_fieldy);
12499       element = Tile[x][y];
12500
12501       if (!IS_PLAYER(x,y) &&
12502           (element == EL_EMPTY ||
12503            CAN_GROW_INTO(element) ||
12504            element == EL_QUICKSAND_EMPTY ||
12505            element == EL_QUICKSAND_FAST_EMPTY ||
12506            element == EL_ACID_SPLASH_LEFT ||
12507            element == EL_ACID_SPLASH_RIGHT))
12508       {
12509         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12510             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12511             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12512             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12513           Tile[x][y] = EL_AMOEBA_DROP;
12514       }
12515
12516       random = random * 129 + 1;
12517     }
12518   }
12519 #endif
12520
12521   game.explosions_delayed = FALSE;
12522
12523   SCAN_PLAYFIELD(x, y)
12524   {
12525     element = Tile[x][y];
12526
12527     if (ExplodeField[x][y])
12528       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12529     else if (element == EL_EXPLOSION)
12530       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12531
12532     ExplodeField[x][y] = EX_TYPE_NONE;
12533   }
12534
12535   game.explosions_delayed = TRUE;
12536
12537   if (game.magic_wall_active)
12538   {
12539     if (!(game.magic_wall_time_left % 4))
12540     {
12541       int element = Tile[magic_wall_x][magic_wall_y];
12542
12543       if (element == EL_BD_MAGIC_WALL_FULL ||
12544           element == EL_BD_MAGIC_WALL_ACTIVE ||
12545           element == EL_BD_MAGIC_WALL_EMPTYING)
12546         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12547       else if (element == EL_DC_MAGIC_WALL_FULL ||
12548                element == EL_DC_MAGIC_WALL_ACTIVE ||
12549                element == EL_DC_MAGIC_WALL_EMPTYING)
12550         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12551       else
12552         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12553     }
12554
12555     if (game.magic_wall_time_left > 0)
12556     {
12557       game.magic_wall_time_left--;
12558
12559       if (!game.magic_wall_time_left)
12560       {
12561         SCAN_PLAYFIELD(x, y)
12562         {
12563           element = Tile[x][y];
12564
12565           if (element == EL_MAGIC_WALL_ACTIVE ||
12566               element == EL_MAGIC_WALL_FULL)
12567           {
12568             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12569             TEST_DrawLevelField(x, y);
12570           }
12571           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12572                    element == EL_BD_MAGIC_WALL_FULL)
12573           {
12574             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12575             TEST_DrawLevelField(x, y);
12576           }
12577           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12578                    element == EL_DC_MAGIC_WALL_FULL)
12579           {
12580             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12581             TEST_DrawLevelField(x, y);
12582           }
12583         }
12584
12585         game.magic_wall_active = FALSE;
12586       }
12587     }
12588   }
12589
12590   if (game.light_time_left > 0)
12591   {
12592     game.light_time_left--;
12593
12594     if (game.light_time_left == 0)
12595       RedrawAllLightSwitchesAndInvisibleElements();
12596   }
12597
12598   if (game.timegate_time_left > 0)
12599   {
12600     game.timegate_time_left--;
12601
12602     if (game.timegate_time_left == 0)
12603       CloseAllOpenTimegates();
12604   }
12605
12606   if (game.lenses_time_left > 0)
12607   {
12608     game.lenses_time_left--;
12609
12610     if (game.lenses_time_left == 0)
12611       RedrawAllInvisibleElementsForLenses();
12612   }
12613
12614   if (game.magnify_time_left > 0)
12615   {
12616     game.magnify_time_left--;
12617
12618     if (game.magnify_time_left == 0)
12619       RedrawAllInvisibleElementsForMagnifier();
12620   }
12621
12622   for (i = 0; i < MAX_PLAYERS; i++)
12623   {
12624     struct PlayerInfo *player = &stored_player[i];
12625
12626     if (SHIELD_ON(player))
12627     {
12628       if (player->shield_deadly_time_left)
12629         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12630       else if (player->shield_normal_time_left)
12631         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12632     }
12633   }
12634
12635 #if USE_DELAYED_GFX_REDRAW
12636   SCAN_PLAYFIELD(x, y)
12637   {
12638     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12639     {
12640       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12641          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12642
12643       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12644         DrawLevelField(x, y);
12645
12646       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12647         DrawLevelFieldCrumbled(x, y);
12648
12649       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12650         DrawLevelFieldCrumbledNeighbours(x, y);
12651
12652       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12653         DrawTwinkleOnField(x, y);
12654     }
12655
12656     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12657   }
12658 #endif
12659
12660   DrawAllPlayers();
12661   PlayAllPlayersSound();
12662
12663   for (i = 0; i < MAX_PLAYERS; i++)
12664   {
12665     struct PlayerInfo *player = &stored_player[i];
12666
12667     if (player->show_envelope != 0 && (!player->active ||
12668                                        player->MovPos == 0))
12669     {
12670       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12671
12672       player->show_envelope = 0;
12673     }
12674   }
12675
12676   // use random number generator in every frame to make it less predictable
12677   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12678     RND(1);
12679
12680   mouse_action_last = mouse_action;
12681 }
12682
12683 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12684 {
12685   int min_x = x, min_y = y, max_x = x, max_y = y;
12686   int scr_fieldx = getScreenFieldSizeX();
12687   int scr_fieldy = getScreenFieldSizeY();
12688   int i;
12689
12690   for (i = 0; i < MAX_PLAYERS; i++)
12691   {
12692     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12693
12694     if (!stored_player[i].active || &stored_player[i] == player)
12695       continue;
12696
12697     min_x = MIN(min_x, jx);
12698     min_y = MIN(min_y, jy);
12699     max_x = MAX(max_x, jx);
12700     max_y = MAX(max_y, jy);
12701   }
12702
12703   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12704 }
12705
12706 static boolean AllPlayersInVisibleScreen(void)
12707 {
12708   int i;
12709
12710   for (i = 0; i < MAX_PLAYERS; i++)
12711   {
12712     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12713
12714     if (!stored_player[i].active)
12715       continue;
12716
12717     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12718       return FALSE;
12719   }
12720
12721   return TRUE;
12722 }
12723
12724 void ScrollLevel(int dx, int dy)
12725 {
12726   int scroll_offset = 2 * TILEX_VAR;
12727   int x, y;
12728
12729   BlitBitmap(drawto_field, drawto_field,
12730              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12731              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12732              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12733              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12734              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12735              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12736
12737   if (dx != 0)
12738   {
12739     x = (dx == 1 ? BX1 : BX2);
12740     for (y = BY1; y <= BY2; y++)
12741       DrawScreenField(x, y);
12742   }
12743
12744   if (dy != 0)
12745   {
12746     y = (dy == 1 ? BY1 : BY2);
12747     for (x = BX1; x <= BX2; x++)
12748       DrawScreenField(x, y);
12749   }
12750
12751   redraw_mask |= REDRAW_FIELD;
12752 }
12753
12754 static boolean canFallDown(struct PlayerInfo *player)
12755 {
12756   int jx = player->jx, jy = player->jy;
12757
12758   return (IN_LEV_FIELD(jx, jy + 1) &&
12759           (IS_FREE(jx, jy + 1) ||
12760            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12761           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12762           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12763 }
12764
12765 static boolean canPassField(int x, int y, int move_dir)
12766 {
12767   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12768   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12769   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12770   int nextx = x + dx;
12771   int nexty = y + dy;
12772   int element = Tile[x][y];
12773
12774   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12775           !CAN_MOVE(element) &&
12776           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12777           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12778           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12779 }
12780
12781 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12782 {
12783   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12784   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12785   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12786   int newx = x + dx;
12787   int newy = y + dy;
12788
12789   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12790           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12791           (IS_DIGGABLE(Tile[newx][newy]) ||
12792            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12793            canPassField(newx, newy, move_dir)));
12794 }
12795
12796 static void CheckGravityMovement(struct PlayerInfo *player)
12797 {
12798   if (player->gravity && !player->programmed_action)
12799   {
12800     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12801     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12802     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12803     int jx = player->jx, jy = player->jy;
12804     boolean player_is_moving_to_valid_field =
12805       (!player_is_snapping &&
12806        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12807         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12808     boolean player_can_fall_down = canFallDown(player);
12809
12810     if (player_can_fall_down &&
12811         !player_is_moving_to_valid_field)
12812       player->programmed_action = MV_DOWN;
12813   }
12814 }
12815
12816 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12817 {
12818   return CheckGravityMovement(player);
12819
12820   if (player->gravity && !player->programmed_action)
12821   {
12822     int jx = player->jx, jy = player->jy;
12823     boolean field_under_player_is_free =
12824       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12825     boolean player_is_standing_on_valid_field =
12826       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12827        (IS_WALKABLE(Tile[jx][jy]) &&
12828         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12829
12830     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12831       player->programmed_action = MV_DOWN;
12832   }
12833 }
12834
12835 /*
12836   MovePlayerOneStep()
12837   -----------------------------------------------------------------------------
12838   dx, dy:               direction (non-diagonal) to try to move the player to
12839   real_dx, real_dy:     direction as read from input device (can be diagonal)
12840 */
12841
12842 boolean MovePlayerOneStep(struct PlayerInfo *player,
12843                           int dx, int dy, int real_dx, int real_dy)
12844 {
12845   int jx = player->jx, jy = player->jy;
12846   int new_jx = jx + dx, new_jy = jy + dy;
12847   int can_move;
12848   boolean player_can_move = !player->cannot_move;
12849
12850   if (!player->active || (!dx && !dy))
12851     return MP_NO_ACTION;
12852
12853   player->MovDir = (dx < 0 ? MV_LEFT :
12854                     dx > 0 ? MV_RIGHT :
12855                     dy < 0 ? MV_UP :
12856                     dy > 0 ? MV_DOWN :  MV_NONE);
12857
12858   if (!IN_LEV_FIELD(new_jx, new_jy))
12859     return MP_NO_ACTION;
12860
12861   if (!player_can_move)
12862   {
12863     if (player->MovPos == 0)
12864     {
12865       player->is_moving = FALSE;
12866       player->is_digging = FALSE;
12867       player->is_collecting = FALSE;
12868       player->is_snapping = FALSE;
12869       player->is_pushing = FALSE;
12870     }
12871   }
12872
12873   if (!network.enabled && game.centered_player_nr == -1 &&
12874       !AllPlayersInSight(player, new_jx, new_jy))
12875     return MP_NO_ACTION;
12876
12877   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12878   if (can_move != MP_MOVING)
12879     return can_move;
12880
12881   // check if DigField() has caused relocation of the player
12882   if (player->jx != jx || player->jy != jy)
12883     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12884
12885   StorePlayer[jx][jy] = 0;
12886   player->last_jx = jx;
12887   player->last_jy = jy;
12888   player->jx = new_jx;
12889   player->jy = new_jy;
12890   StorePlayer[new_jx][new_jy] = player->element_nr;
12891
12892   if (player->move_delay_value_next != -1)
12893   {
12894     player->move_delay_value = player->move_delay_value_next;
12895     player->move_delay_value_next = -1;
12896   }
12897
12898   player->MovPos =
12899     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12900
12901   player->step_counter++;
12902
12903   PlayerVisit[jx][jy] = FrameCounter;
12904
12905   player->is_moving = TRUE;
12906
12907 #if 1
12908   // should better be called in MovePlayer(), but this breaks some tapes
12909   ScrollPlayer(player, SCROLL_INIT);
12910 #endif
12911
12912   return MP_MOVING;
12913 }
12914
12915 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12916 {
12917   int jx = player->jx, jy = player->jy;
12918   int old_jx = jx, old_jy = jy;
12919   int moved = MP_NO_ACTION;
12920
12921   if (!player->active)
12922     return FALSE;
12923
12924   if (!dx && !dy)
12925   {
12926     if (player->MovPos == 0)
12927     {
12928       player->is_moving = FALSE;
12929       player->is_digging = FALSE;
12930       player->is_collecting = FALSE;
12931       player->is_snapping = FALSE;
12932       player->is_pushing = FALSE;
12933     }
12934
12935     return FALSE;
12936   }
12937
12938   if (player->move_delay > 0)
12939     return FALSE;
12940
12941   player->move_delay = -1;              // set to "uninitialized" value
12942
12943   // store if player is automatically moved to next field
12944   player->is_auto_moving = (player->programmed_action != MV_NONE);
12945
12946   // remove the last programmed player action
12947   player->programmed_action = 0;
12948
12949   if (player->MovPos)
12950   {
12951     // should only happen if pre-1.2 tape recordings are played
12952     // this is only for backward compatibility
12953
12954     int original_move_delay_value = player->move_delay_value;
12955
12956 #if DEBUG
12957     Debug("game:playing:MovePlayer",
12958           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12959           tape.counter);
12960 #endif
12961
12962     // scroll remaining steps with finest movement resolution
12963     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12964
12965     while (player->MovPos)
12966     {
12967       ScrollPlayer(player, SCROLL_GO_ON);
12968       ScrollScreen(NULL, SCROLL_GO_ON);
12969
12970       AdvanceFrameAndPlayerCounters(player->index_nr);
12971
12972       DrawAllPlayers();
12973       BackToFront_WithFrameDelay(0);
12974     }
12975
12976     player->move_delay_value = original_move_delay_value;
12977   }
12978
12979   player->is_active = FALSE;
12980
12981   if (player->last_move_dir & MV_HORIZONTAL)
12982   {
12983     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12984       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12985   }
12986   else
12987   {
12988     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12989       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12990   }
12991
12992   if (!moved && !player->is_active)
12993   {
12994     player->is_moving = FALSE;
12995     player->is_digging = FALSE;
12996     player->is_collecting = FALSE;
12997     player->is_snapping = FALSE;
12998     player->is_pushing = FALSE;
12999   }
13000
13001   jx = player->jx;
13002   jy = player->jy;
13003
13004   if (moved & MP_MOVING && !ScreenMovPos &&
13005       (player->index_nr == game.centered_player_nr ||
13006        game.centered_player_nr == -1))
13007   {
13008     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13009
13010     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13011     {
13012       // actual player has left the screen -- scroll in that direction
13013       if (jx != old_jx)         // player has moved horizontally
13014         scroll_x += (jx - old_jx);
13015       else                      // player has moved vertically
13016         scroll_y += (jy - old_jy);
13017     }
13018     else
13019     {
13020       int offset_raw = game.scroll_delay_value;
13021
13022       if (jx != old_jx)         // player has moved horizontally
13023       {
13024         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13025         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13026         int new_scroll_x = jx - MIDPOSX + offset_x;
13027
13028         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13029             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13030           scroll_x = new_scroll_x;
13031
13032         // don't scroll over playfield boundaries
13033         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13034
13035         // don't scroll more than one field at a time
13036         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13037
13038         // don't scroll against the player's moving direction
13039         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13040             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13041           scroll_x = old_scroll_x;
13042       }
13043       else                      // player has moved vertically
13044       {
13045         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13046         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13047         int new_scroll_y = jy - MIDPOSY + offset_y;
13048
13049         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13050             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13051           scroll_y = new_scroll_y;
13052
13053         // don't scroll over playfield boundaries
13054         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13055
13056         // don't scroll more than one field at a time
13057         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13058
13059         // don't scroll against the player's moving direction
13060         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13061             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13062           scroll_y = old_scroll_y;
13063       }
13064     }
13065
13066     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13067     {
13068       if (!network.enabled && game.centered_player_nr == -1 &&
13069           !AllPlayersInVisibleScreen())
13070       {
13071         scroll_x = old_scroll_x;
13072         scroll_y = old_scroll_y;
13073       }
13074       else
13075       {
13076         ScrollScreen(player, SCROLL_INIT);
13077         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13078       }
13079     }
13080   }
13081
13082   player->StepFrame = 0;
13083
13084   if (moved & MP_MOVING)
13085   {
13086     if (old_jx != jx && old_jy == jy)
13087       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13088     else if (old_jx == jx && old_jy != jy)
13089       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13090
13091     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13092
13093     player->last_move_dir = player->MovDir;
13094     player->is_moving = TRUE;
13095     player->is_snapping = FALSE;
13096     player->is_switching = FALSE;
13097     player->is_dropping = FALSE;
13098     player->is_dropping_pressed = FALSE;
13099     player->drop_pressed_delay = 0;
13100
13101 #if 0
13102     // should better be called here than above, but this breaks some tapes
13103     ScrollPlayer(player, SCROLL_INIT);
13104 #endif
13105   }
13106   else
13107   {
13108     CheckGravityMovementWhenNotMoving(player);
13109
13110     player->is_moving = FALSE;
13111
13112     /* at this point, the player is allowed to move, but cannot move right now
13113        (e.g. because of something blocking the way) -- ensure that the player
13114        is also allowed to move in the next frame (in old versions before 3.1.1,
13115        the player was forced to wait again for eight frames before next try) */
13116
13117     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13118       player->move_delay = 0;   // allow direct movement in the next frame
13119   }
13120
13121   if (player->move_delay == -1)         // not yet initialized by DigField()
13122     player->move_delay = player->move_delay_value;
13123
13124   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13125   {
13126     TestIfPlayerTouchesBadThing(jx, jy);
13127     TestIfPlayerTouchesCustomElement(jx, jy);
13128   }
13129
13130   if (!player->active)
13131     RemovePlayer(player);
13132
13133   return moved;
13134 }
13135
13136 void ScrollPlayer(struct PlayerInfo *player, int mode)
13137 {
13138   int jx = player->jx, jy = player->jy;
13139   int last_jx = player->last_jx, last_jy = player->last_jy;
13140   int move_stepsize = TILEX / player->move_delay_value;
13141
13142   if (!player->active)
13143     return;
13144
13145   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13146     return;
13147
13148   if (mode == SCROLL_INIT)
13149   {
13150     player->actual_frame_counter.count = FrameCounter;
13151     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13152
13153     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13154         Tile[last_jx][last_jy] == EL_EMPTY)
13155     {
13156       int last_field_block_delay = 0;   // start with no blocking at all
13157       int block_delay_adjustment = player->block_delay_adjustment;
13158
13159       // if player blocks last field, add delay for exactly one move
13160       if (player->block_last_field)
13161       {
13162         last_field_block_delay += player->move_delay_value;
13163
13164         // when blocking enabled, prevent moving up despite gravity
13165         if (player->gravity && player->MovDir == MV_UP)
13166           block_delay_adjustment = -1;
13167       }
13168
13169       // add block delay adjustment (also possible when not blocking)
13170       last_field_block_delay += block_delay_adjustment;
13171
13172       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13173       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13174     }
13175
13176     if (player->MovPos != 0)    // player has not yet reached destination
13177       return;
13178   }
13179   else if (!FrameReached(&player->actual_frame_counter))
13180     return;
13181
13182   if (player->MovPos != 0)
13183   {
13184     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13185     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13186
13187     // before DrawPlayer() to draw correct player graphic for this case
13188     if (player->MovPos == 0)
13189       CheckGravityMovement(player);
13190   }
13191
13192   if (player->MovPos == 0)      // player reached destination field
13193   {
13194     if (player->move_delay_reset_counter > 0)
13195     {
13196       player->move_delay_reset_counter--;
13197
13198       if (player->move_delay_reset_counter == 0)
13199       {
13200         // continue with normal speed after quickly moving through gate
13201         HALVE_PLAYER_SPEED(player);
13202
13203         // be able to make the next move without delay
13204         player->move_delay = 0;
13205       }
13206     }
13207
13208     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13209         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13210         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13211         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13212         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13213         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13214         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13215         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13216     {
13217       ExitPlayer(player);
13218
13219       if (game.players_still_needed == 0 &&
13220           (game.friends_still_needed == 0 ||
13221            IS_SP_ELEMENT(Tile[jx][jy])))
13222         LevelSolved();
13223     }
13224
13225     player->last_jx = jx;
13226     player->last_jy = jy;
13227
13228     // this breaks one level: "machine", level 000
13229     {
13230       int move_direction = player->MovDir;
13231       int enter_side = MV_DIR_OPPOSITE(move_direction);
13232       int leave_side = move_direction;
13233       int old_jx = last_jx;
13234       int old_jy = last_jy;
13235       int old_element = Tile[old_jx][old_jy];
13236       int new_element = Tile[jx][jy];
13237
13238       if (IS_CUSTOM_ELEMENT(old_element))
13239         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13240                                    CE_LEFT_BY_PLAYER,
13241                                    player->index_bit, leave_side);
13242
13243       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13244                                           CE_PLAYER_LEAVES_X,
13245                                           player->index_bit, leave_side);
13246
13247       if (IS_CUSTOM_ELEMENT(new_element))
13248         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13249                                    player->index_bit, enter_side);
13250
13251       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13252                                           CE_PLAYER_ENTERS_X,
13253                                           player->index_bit, enter_side);
13254
13255       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13256                                         CE_MOVE_OF_X, move_direction);
13257     }
13258
13259     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13260     {
13261       TestIfPlayerTouchesBadThing(jx, jy);
13262       TestIfPlayerTouchesCustomElement(jx, jy);
13263
13264       /* needed because pushed element has not yet reached its destination,
13265          so it would trigger a change event at its previous field location */
13266       if (!player->is_pushing)
13267         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13268
13269       if (level.finish_dig_collect &&
13270           (player->is_digging || player->is_collecting))
13271       {
13272         int last_element = player->last_removed_element;
13273         int move_direction = player->MovDir;
13274         int enter_side = MV_DIR_OPPOSITE(move_direction);
13275         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13276                             CE_PLAYER_COLLECTS_X);
13277
13278         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13279                                             player->index_bit, enter_side);
13280
13281         player->last_removed_element = EL_UNDEFINED;
13282       }
13283
13284       if (!player->active)
13285         RemovePlayer(player);
13286     }
13287
13288     if (level.use_step_counter)
13289       CheckLevelTime_StepCounter();
13290
13291     if (tape.single_step && tape.recording && !tape.pausing &&
13292         !player->programmed_action)
13293       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13294
13295     if (!player->programmed_action)
13296       CheckSaveEngineSnapshot(player);
13297   }
13298 }
13299
13300 void ScrollScreen(struct PlayerInfo *player, int mode)
13301 {
13302   static DelayCounter screen_frame_counter = { 0 };
13303
13304   if (mode == SCROLL_INIT)
13305   {
13306     // set scrolling step size according to actual player's moving speed
13307     ScrollStepSize = TILEX / player->move_delay_value;
13308
13309     screen_frame_counter.count = FrameCounter;
13310     screen_frame_counter.value = 1;
13311
13312     ScreenMovDir = player->MovDir;
13313     ScreenMovPos = player->MovPos;
13314     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13315     return;
13316   }
13317   else if (!FrameReached(&screen_frame_counter))
13318     return;
13319
13320   if (ScreenMovPos)
13321   {
13322     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13323     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13324     redraw_mask |= REDRAW_FIELD;
13325   }
13326   else
13327     ScreenMovDir = MV_NONE;
13328 }
13329
13330 void CheckNextToConditions(int x, int y)
13331 {
13332   int element = Tile[x][y];
13333
13334   if (IS_PLAYER(x, y))
13335     TestIfPlayerNextToCustomElement(x, y);
13336
13337   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13338       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13339     TestIfElementNextToCustomElement(x, y);
13340 }
13341
13342 void TestIfPlayerNextToCustomElement(int x, int y)
13343 {
13344   struct XY *xy = xy_topdown;
13345   static int trigger_sides[4][2] =
13346   {
13347     // center side       border side
13348     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13349     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13350     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13351     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13352   };
13353   int i;
13354
13355   if (!IS_PLAYER(x, y))
13356     return;
13357
13358   struct PlayerInfo *player = PLAYERINFO(x, y);
13359
13360   if (player->is_moving)
13361     return;
13362
13363   for (i = 0; i < NUM_DIRECTIONS; i++)
13364   {
13365     int xx = x + xy[i].x;
13366     int yy = y + xy[i].y;
13367     int border_side = trigger_sides[i][1];
13368     int border_element;
13369
13370     if (!IN_LEV_FIELD(xx, yy))
13371       continue;
13372
13373     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13374       continue;         // center and border element not connected
13375
13376     border_element = Tile[xx][yy];
13377
13378     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13379                                player->index_bit, border_side);
13380     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13381                                         CE_PLAYER_NEXT_TO_X,
13382                                         player->index_bit, border_side);
13383
13384     /* use player element that is initially defined in the level playfield,
13385        not the player element that corresponds to the runtime player number
13386        (example: a level that contains EL_PLAYER_3 as the only player would
13387        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13388
13389     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13390                              CE_NEXT_TO_X, border_side);
13391   }
13392 }
13393
13394 void TestIfPlayerTouchesCustomElement(int x, int y)
13395 {
13396   struct XY *xy = xy_topdown;
13397   static int trigger_sides[4][2] =
13398   {
13399     // center side       border side
13400     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13401     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13402     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13403     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13404   };
13405   static int touch_dir[4] =
13406   {
13407     MV_LEFT | MV_RIGHT,
13408     MV_UP   | MV_DOWN,
13409     MV_UP   | MV_DOWN,
13410     MV_LEFT | MV_RIGHT
13411   };
13412   int center_element = Tile[x][y];      // should always be non-moving!
13413   int i;
13414
13415   for (i = 0; i < NUM_DIRECTIONS; i++)
13416   {
13417     int xx = x + xy[i].x;
13418     int yy = y + xy[i].y;
13419     int center_side = trigger_sides[i][0];
13420     int border_side = trigger_sides[i][1];
13421     int border_element;
13422
13423     if (!IN_LEV_FIELD(xx, yy))
13424       continue;
13425
13426     if (IS_PLAYER(x, y))                // player found at center element
13427     {
13428       struct PlayerInfo *player = PLAYERINFO(x, y);
13429
13430       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13431         border_element = Tile[xx][yy];          // may be moving!
13432       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13433         border_element = Tile[xx][yy];
13434       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13435         border_element = MovingOrBlocked2Element(xx, yy);
13436       else
13437         continue;               // center and border element do not touch
13438
13439       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13440                                  player->index_bit, border_side);
13441       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13442                                           CE_PLAYER_TOUCHES_X,
13443                                           player->index_bit, border_side);
13444
13445       {
13446         /* use player element that is initially defined in the level playfield,
13447            not the player element that corresponds to the runtime player number
13448            (example: a level that contains EL_PLAYER_3 as the only player would
13449            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13450         int player_element = PLAYERINFO(x, y)->initial_element;
13451
13452         CheckElementChangeBySide(xx, yy, border_element, player_element,
13453                                  CE_TOUCHING_X, border_side);
13454       }
13455     }
13456     else if (IS_PLAYER(xx, yy))         // player found at border element
13457     {
13458       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13459
13460       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13461       {
13462         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13463           continue;             // center and border element do not touch
13464       }
13465
13466       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13467                                  player->index_bit, center_side);
13468       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13469                                           CE_PLAYER_TOUCHES_X,
13470                                           player->index_bit, center_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(xx, yy)->initial_element;
13478
13479         CheckElementChangeBySide(x, y, center_element, player_element,
13480                                  CE_TOUCHING_X, center_side);
13481       }
13482
13483       break;
13484     }
13485   }
13486 }
13487
13488 void TestIfElementNextToCustomElement(int x, int y)
13489 {
13490   struct XY *xy = xy_topdown;
13491   static int trigger_sides[4][2] =
13492   {
13493     // center side      border side
13494     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13495     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13496     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13497     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13498   };
13499   int center_element = Tile[x][y];      // should always be non-moving!
13500   int i;
13501
13502   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13503     return;
13504
13505   for (i = 0; i < NUM_DIRECTIONS; i++)
13506   {
13507     int xx = x + xy[i].x;
13508     int yy = y + xy[i].y;
13509     int border_side = trigger_sides[i][1];
13510     int border_element;
13511
13512     if (!IN_LEV_FIELD(xx, yy))
13513       continue;
13514
13515     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13516       continue;                 // center and border element not connected
13517
13518     border_element = Tile[xx][yy];
13519
13520     // check for change of center element (but change it only once)
13521     if (CheckElementChangeBySide(x, y, center_element, border_element,
13522                                  CE_NEXT_TO_X, border_side))
13523       break;
13524   }
13525 }
13526
13527 void TestIfElementTouchesCustomElement(int x, int y)
13528 {
13529   struct XY *xy = xy_topdown;
13530   static int trigger_sides[4][2] =
13531   {
13532     // center side      border side
13533     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13534     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13535     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13536     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13537   };
13538   static int touch_dir[4] =
13539   {
13540     MV_LEFT | MV_RIGHT,
13541     MV_UP   | MV_DOWN,
13542     MV_UP   | MV_DOWN,
13543     MV_LEFT | MV_RIGHT
13544   };
13545   boolean change_center_element = FALSE;
13546   int center_element = Tile[x][y];      // should always be non-moving!
13547   int border_element_old[NUM_DIRECTIONS];
13548   int i;
13549
13550   for (i = 0; i < NUM_DIRECTIONS; i++)
13551   {
13552     int xx = x + xy[i].x;
13553     int yy = y + xy[i].y;
13554     int border_element;
13555
13556     border_element_old[i] = -1;
13557
13558     if (!IN_LEV_FIELD(xx, yy))
13559       continue;
13560
13561     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13562       border_element = Tile[xx][yy];    // may be moving!
13563     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13564       border_element = Tile[xx][yy];
13565     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13566       border_element = MovingOrBlocked2Element(xx, yy);
13567     else
13568       continue;                 // center and border element do not touch
13569
13570     border_element_old[i] = border_element;
13571   }
13572
13573   for (i = 0; i < NUM_DIRECTIONS; i++)
13574   {
13575     int xx = x + xy[i].x;
13576     int yy = y + xy[i].y;
13577     int center_side = trigger_sides[i][0];
13578     int border_element = border_element_old[i];
13579
13580     if (border_element == -1)
13581       continue;
13582
13583     // check for change of border element
13584     CheckElementChangeBySide(xx, yy, border_element, center_element,
13585                              CE_TOUCHING_X, center_side);
13586
13587     // (center element cannot be player, so we dont have to check this here)
13588   }
13589
13590   for (i = 0; i < NUM_DIRECTIONS; i++)
13591   {
13592     int xx = x + xy[i].x;
13593     int yy = y + xy[i].y;
13594     int border_side = trigger_sides[i][1];
13595     int border_element = border_element_old[i];
13596
13597     if (border_element == -1)
13598       continue;
13599
13600     // check for change of center element (but change it only once)
13601     if (!change_center_element)
13602       change_center_element =
13603         CheckElementChangeBySide(x, y, center_element, border_element,
13604                                  CE_TOUCHING_X, border_side);
13605
13606     if (IS_PLAYER(xx, yy))
13607     {
13608       /* use player element that is initially defined in the level playfield,
13609          not the player element that corresponds to the runtime player number
13610          (example: a level that contains EL_PLAYER_3 as the only player would
13611          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13612       int player_element = PLAYERINFO(xx, yy)->initial_element;
13613
13614       CheckElementChangeBySide(x, y, center_element, player_element,
13615                                CE_TOUCHING_X, border_side);
13616     }
13617   }
13618 }
13619
13620 void TestIfElementHitsCustomElement(int x, int y, int direction)
13621 {
13622   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13623   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13624   int hitx = x + dx, hity = y + dy;
13625   int hitting_element = Tile[x][y];
13626   int touched_element;
13627
13628   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13629     return;
13630
13631   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13632                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13633
13634   if (IN_LEV_FIELD(hitx, hity))
13635   {
13636     int opposite_direction = MV_DIR_OPPOSITE(direction);
13637     int hitting_side = direction;
13638     int touched_side = opposite_direction;
13639     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13640                           MovDir[hitx][hity] != direction ||
13641                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13642
13643     object_hit = TRUE;
13644
13645     if (object_hit)
13646     {
13647       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13648                                CE_HITTING_X, touched_side);
13649
13650       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13651                                CE_HIT_BY_X, hitting_side);
13652
13653       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13654                                CE_HIT_BY_SOMETHING, opposite_direction);
13655
13656       if (IS_PLAYER(hitx, hity))
13657       {
13658         /* use player element that is initially defined in the level playfield,
13659            not the player element that corresponds to the runtime player number
13660            (example: a level that contains EL_PLAYER_3 as the only player would
13661            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13662         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13663
13664         CheckElementChangeBySide(x, y, hitting_element, player_element,
13665                                  CE_HITTING_X, touched_side);
13666       }
13667     }
13668   }
13669
13670   // "hitting something" is also true when hitting the playfield border
13671   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13672                            CE_HITTING_SOMETHING, direction);
13673 }
13674
13675 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13676 {
13677   int i, kill_x = -1, kill_y = -1;
13678
13679   int bad_element = -1;
13680   struct XY *test_xy = xy_topdown;
13681   static int test_dir[4] =
13682   {
13683     MV_UP,
13684     MV_LEFT,
13685     MV_RIGHT,
13686     MV_DOWN
13687   };
13688
13689   for (i = 0; i < NUM_DIRECTIONS; i++)
13690   {
13691     int test_x, test_y, test_move_dir, test_element;
13692
13693     test_x = good_x + test_xy[i].x;
13694     test_y = good_y + test_xy[i].y;
13695
13696     if (!IN_LEV_FIELD(test_x, test_y))
13697       continue;
13698
13699     test_move_dir =
13700       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13701
13702     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13703
13704     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13705        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13706     */
13707     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13708         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13709     {
13710       kill_x = test_x;
13711       kill_y = test_y;
13712       bad_element = test_element;
13713
13714       break;
13715     }
13716   }
13717
13718   if (kill_x != -1 || kill_y != -1)
13719   {
13720     if (IS_PLAYER(good_x, good_y))
13721     {
13722       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13723
13724       if (player->shield_deadly_time_left > 0 &&
13725           !IS_INDESTRUCTIBLE(bad_element))
13726         Bang(kill_x, kill_y);
13727       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13728         KillPlayer(player);
13729     }
13730     else
13731       Bang(good_x, good_y);
13732   }
13733 }
13734
13735 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13736 {
13737   int i, kill_x = -1, kill_y = -1;
13738   int bad_element = Tile[bad_x][bad_y];
13739   struct XY *test_xy = xy_topdown;
13740   static int touch_dir[4] =
13741   {
13742     MV_LEFT | MV_RIGHT,
13743     MV_UP   | MV_DOWN,
13744     MV_UP   | MV_DOWN,
13745     MV_LEFT | MV_RIGHT
13746   };
13747   static int test_dir[4] =
13748   {
13749     MV_UP,
13750     MV_LEFT,
13751     MV_RIGHT,
13752     MV_DOWN
13753   };
13754
13755   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13756     return;
13757
13758   for (i = 0; i < NUM_DIRECTIONS; i++)
13759   {
13760     int test_x, test_y, test_move_dir, test_element;
13761
13762     test_x = bad_x + test_xy[i].x;
13763     test_y = bad_y + test_xy[i].y;
13764
13765     if (!IN_LEV_FIELD(test_x, test_y))
13766       continue;
13767
13768     test_move_dir =
13769       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13770
13771     test_element = Tile[test_x][test_y];
13772
13773     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13774        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13775     */
13776     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13777         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13778     {
13779       // good thing is player or penguin that does not move away
13780       if (IS_PLAYER(test_x, test_y))
13781       {
13782         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13783
13784         if (bad_element == EL_ROBOT && player->is_moving)
13785           continue;     // robot does not kill player if he is moving
13786
13787         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13788         {
13789           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13790             continue;           // center and border element do not touch
13791         }
13792
13793         kill_x = test_x;
13794         kill_y = test_y;
13795
13796         break;
13797       }
13798       else if (test_element == EL_PENGUIN)
13799       {
13800         kill_x = test_x;
13801         kill_y = test_y;
13802
13803         break;
13804       }
13805     }
13806   }
13807
13808   if (kill_x != -1 || kill_y != -1)
13809   {
13810     if (IS_PLAYER(kill_x, kill_y))
13811     {
13812       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13813
13814       if (player->shield_deadly_time_left > 0 &&
13815           !IS_INDESTRUCTIBLE(bad_element))
13816         Bang(bad_x, bad_y);
13817       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13818         KillPlayer(player);
13819     }
13820     else
13821       Bang(kill_x, kill_y);
13822   }
13823 }
13824
13825 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13826 {
13827   int bad_element = Tile[bad_x][bad_y];
13828   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13829   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13830   int test_x = bad_x + dx, test_y = bad_y + dy;
13831   int test_move_dir, test_element;
13832   int kill_x = -1, kill_y = -1;
13833
13834   if (!IN_LEV_FIELD(test_x, test_y))
13835     return;
13836
13837   test_move_dir =
13838     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13839
13840   test_element = Tile[test_x][test_y];
13841
13842   if (test_move_dir != bad_move_dir)
13843   {
13844     // good thing can be player or penguin that does not move away
13845     if (IS_PLAYER(test_x, test_y))
13846     {
13847       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13848
13849       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13850          player as being hit when he is moving towards the bad thing, because
13851          the "get hit by" condition would be lost after the player stops) */
13852       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13853         return;         // player moves away from bad thing
13854
13855       kill_x = test_x;
13856       kill_y = test_y;
13857     }
13858     else if (test_element == EL_PENGUIN)
13859     {
13860       kill_x = test_x;
13861       kill_y = test_y;
13862     }
13863   }
13864
13865   if (kill_x != -1 || kill_y != -1)
13866   {
13867     if (IS_PLAYER(kill_x, kill_y))
13868     {
13869       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13870
13871       if (player->shield_deadly_time_left > 0 &&
13872           !IS_INDESTRUCTIBLE(bad_element))
13873         Bang(bad_x, bad_y);
13874       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13875         KillPlayer(player);
13876     }
13877     else
13878       Bang(kill_x, kill_y);
13879   }
13880 }
13881
13882 void TestIfPlayerTouchesBadThing(int x, int y)
13883 {
13884   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13885 }
13886
13887 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13888 {
13889   TestIfGoodThingHitsBadThing(x, y, move_dir);
13890 }
13891
13892 void TestIfBadThingTouchesPlayer(int x, int y)
13893 {
13894   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13895 }
13896
13897 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13898 {
13899   TestIfBadThingHitsGoodThing(x, y, move_dir);
13900 }
13901
13902 void TestIfFriendTouchesBadThing(int x, int y)
13903 {
13904   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13905 }
13906
13907 void TestIfBadThingTouchesFriend(int x, int y)
13908 {
13909   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13910 }
13911
13912 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13913 {
13914   int i, kill_x = bad_x, kill_y = bad_y;
13915   struct XY *xy = xy_topdown;
13916
13917   for (i = 0; i < NUM_DIRECTIONS; i++)
13918   {
13919     int x, y, element;
13920
13921     x = bad_x + xy[i].x;
13922     y = bad_y + xy[i].y;
13923     if (!IN_LEV_FIELD(x, y))
13924       continue;
13925
13926     element = Tile[x][y];
13927     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13928         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13929     {
13930       kill_x = x;
13931       kill_y = y;
13932       break;
13933     }
13934   }
13935
13936   if (kill_x != bad_x || kill_y != bad_y)
13937     Bang(bad_x, bad_y);
13938 }
13939
13940 void KillPlayer(struct PlayerInfo *player)
13941 {
13942   int jx = player->jx, jy = player->jy;
13943
13944   if (!player->active)
13945     return;
13946
13947 #if 0
13948   Debug("game:playing:KillPlayer",
13949         "0: killed == %d, active == %d, reanimated == %d",
13950         player->killed, player->active, player->reanimated);
13951 #endif
13952
13953   /* the following code was introduced to prevent an infinite loop when calling
13954      -> Bang()
13955      -> CheckTriggeredElementChangeExt()
13956      -> ExecuteCustomElementAction()
13957      -> KillPlayer()
13958      -> (infinitely repeating the above sequence of function calls)
13959      which occurs when killing the player while having a CE with the setting
13960      "kill player X when explosion of <player X>"; the solution using a new
13961      field "player->killed" was chosen for backwards compatibility, although
13962      clever use of the fields "player->active" etc. would probably also work */
13963 #if 1
13964   if (player->killed)
13965     return;
13966 #endif
13967
13968   player->killed = TRUE;
13969
13970   // remove accessible field at the player's position
13971   Tile[jx][jy] = EL_EMPTY;
13972
13973   // deactivate shield (else Bang()/Explode() would not work right)
13974   player->shield_normal_time_left = 0;
13975   player->shield_deadly_time_left = 0;
13976
13977 #if 0
13978   Debug("game:playing:KillPlayer",
13979         "1: killed == %d, active == %d, reanimated == %d",
13980         player->killed, player->active, player->reanimated);
13981 #endif
13982
13983   Bang(jx, jy);
13984
13985 #if 0
13986   Debug("game:playing:KillPlayer",
13987         "2: killed == %d, active == %d, reanimated == %d",
13988         player->killed, player->active, player->reanimated);
13989 #endif
13990
13991   if (player->reanimated)       // killed player may have been reanimated
13992     player->killed = player->reanimated = FALSE;
13993   else
13994     BuryPlayer(player);
13995 }
13996
13997 static void KillPlayerUnlessEnemyProtected(int x, int y)
13998 {
13999   if (!PLAYER_ENEMY_PROTECTED(x, y))
14000     KillPlayer(PLAYERINFO(x, y));
14001 }
14002
14003 static void KillPlayerUnlessExplosionProtected(int x, int y)
14004 {
14005   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14006     KillPlayer(PLAYERINFO(x, y));
14007 }
14008
14009 void BuryPlayer(struct PlayerInfo *player)
14010 {
14011   int jx = player->jx, jy = player->jy;
14012
14013   if (!player->active)
14014     return;
14015
14016   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14017   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14018
14019   RemovePlayer(player);
14020
14021   player->buried = TRUE;
14022
14023   if (game.all_players_gone)
14024     game.GameOver = TRUE;
14025 }
14026
14027 void RemovePlayer(struct PlayerInfo *player)
14028 {
14029   int jx = player->jx, jy = player->jy;
14030   int i, found = FALSE;
14031
14032   player->present = FALSE;
14033   player->active = FALSE;
14034
14035   // required for some CE actions (even if the player is not active anymore)
14036   player->MovPos = 0;
14037
14038   if (!ExplodeField[jx][jy])
14039     StorePlayer[jx][jy] = 0;
14040
14041   if (player->is_moving)
14042     TEST_DrawLevelField(player->last_jx, player->last_jy);
14043
14044   for (i = 0; i < MAX_PLAYERS; i++)
14045     if (stored_player[i].active)
14046       found = TRUE;
14047
14048   if (!found)
14049   {
14050     game.all_players_gone = TRUE;
14051     game.GameOver = TRUE;
14052   }
14053
14054   game.exit_x = game.robot_wheel_x = jx;
14055   game.exit_y = game.robot_wheel_y = jy;
14056 }
14057
14058 void ExitPlayer(struct PlayerInfo *player)
14059 {
14060   DrawPlayer(player);   // needed here only to cleanup last field
14061   RemovePlayer(player);
14062
14063   if (game.players_still_needed > 0)
14064     game.players_still_needed--;
14065 }
14066
14067 static void SetFieldForSnapping(int x, int y, int element, int direction,
14068                                 int player_index_bit)
14069 {
14070   struct ElementInfo *ei = &element_info[element];
14071   int direction_bit = MV_DIR_TO_BIT(direction);
14072   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14073   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14074                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14075
14076   Tile[x][y] = EL_ELEMENT_SNAPPING;
14077   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14078   MovDir[x][y] = direction;
14079   Store[x][y] = element;
14080   Store2[x][y] = player_index_bit;
14081
14082   ResetGfxAnimation(x, y);
14083
14084   GfxElement[x][y] = element;
14085   GfxAction[x][y] = action;
14086   GfxDir[x][y] = direction;
14087   GfxFrame[x][y] = -1;
14088 }
14089
14090 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14091                                    int player_index_bit)
14092 {
14093   TestIfElementTouchesCustomElement(x, y);      // for empty space
14094
14095   if (level.finish_dig_collect)
14096   {
14097     int dig_side = MV_DIR_OPPOSITE(direction);
14098     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14099                         CE_PLAYER_COLLECTS_X);
14100
14101     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14102                                         player_index_bit, dig_side);
14103     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14104                                         player_index_bit, dig_side);
14105   }
14106 }
14107
14108 /*
14109   =============================================================================
14110   checkDiagonalPushing()
14111   -----------------------------------------------------------------------------
14112   check if diagonal input device direction results in pushing of object
14113   (by checking if the alternative direction is walkable, diggable, ...)
14114   =============================================================================
14115 */
14116
14117 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14118                                     int x, int y, int real_dx, int real_dy)
14119 {
14120   int jx, jy, dx, dy, xx, yy;
14121
14122   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14123     return TRUE;
14124
14125   // diagonal direction: check alternative direction
14126   jx = player->jx;
14127   jy = player->jy;
14128   dx = x - jx;
14129   dy = y - jy;
14130   xx = jx + (dx == 0 ? real_dx : 0);
14131   yy = jy + (dy == 0 ? real_dy : 0);
14132
14133   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14134 }
14135
14136 /*
14137   =============================================================================
14138   DigField()
14139   -----------------------------------------------------------------------------
14140   x, y:                 field next to player (non-diagonal) to try to dig to
14141   real_dx, real_dy:     direction as read from input device (can be diagonal)
14142   =============================================================================
14143 */
14144
14145 static int DigField(struct PlayerInfo *player,
14146                     int oldx, int oldy, int x, int y,
14147                     int real_dx, int real_dy, int mode)
14148 {
14149   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14150   boolean player_was_pushing = player->is_pushing;
14151   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14152   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14153   int jx = oldx, jy = oldy;
14154   int dx = x - jx, dy = y - jy;
14155   int nextx = x + dx, nexty = y + dy;
14156   int move_direction = (dx == -1 ? MV_LEFT  :
14157                         dx == +1 ? MV_RIGHT :
14158                         dy == -1 ? MV_UP    :
14159                         dy == +1 ? MV_DOWN  : MV_NONE);
14160   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14161   int dig_side = MV_DIR_OPPOSITE(move_direction);
14162   int old_element = Tile[jx][jy];
14163   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14164   int collect_count;
14165
14166   if (is_player)                // function can also be called by EL_PENGUIN
14167   {
14168     if (player->MovPos == 0)
14169     {
14170       player->is_digging = FALSE;
14171       player->is_collecting = FALSE;
14172     }
14173
14174     if (player->MovPos == 0)    // last pushing move finished
14175       player->is_pushing = FALSE;
14176
14177     if (mode == DF_NO_PUSH)     // player just stopped pushing
14178     {
14179       player->is_switching = FALSE;
14180       player->push_delay = -1;
14181
14182       return MP_NO_ACTION;
14183     }
14184   }
14185   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14186     old_element = Back[jx][jy];
14187
14188   // in case of element dropped at player position, check background
14189   else if (Back[jx][jy] != EL_EMPTY &&
14190            game.engine_version >= VERSION_IDENT(2,2,0,0))
14191     old_element = Back[jx][jy];
14192
14193   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14194     return MP_NO_ACTION;        // field has no opening in this direction
14195
14196   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14197     return MP_NO_ACTION;        // field has no opening in this direction
14198
14199   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14200   {
14201     SplashAcid(x, y);
14202
14203     Tile[jx][jy] = player->artwork_element;
14204     InitMovingField(jx, jy, MV_DOWN);
14205     Store[jx][jy] = EL_ACID;
14206     ContinueMoving(jx, jy);
14207     BuryPlayer(player);
14208
14209     return MP_DONT_RUN_INTO;
14210   }
14211
14212   if (player_can_move && DONT_RUN_INTO(element))
14213   {
14214     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14215
14216     return MP_DONT_RUN_INTO;
14217   }
14218
14219   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14220     return MP_NO_ACTION;
14221
14222   collect_count = element_info[element].collect_count_initial;
14223
14224   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14225     return MP_NO_ACTION;
14226
14227   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14228     player_can_move = player_can_move_or_snap;
14229
14230   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14231       game.engine_version >= VERSION_IDENT(2,2,0,0))
14232   {
14233     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14234                                player->index_bit, dig_side);
14235     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14236                                         player->index_bit, dig_side);
14237
14238     if (element == EL_DC_LANDMINE)
14239       Bang(x, y);
14240
14241     if (Tile[x][y] != element)          // field changed by snapping
14242       return MP_ACTION;
14243
14244     return MP_NO_ACTION;
14245   }
14246
14247   if (player->gravity && is_player && !player->is_auto_moving &&
14248       canFallDown(player) && move_direction != MV_DOWN &&
14249       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14250     return MP_NO_ACTION;        // player cannot walk here due to gravity
14251
14252   if (player_can_move &&
14253       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14254   {
14255     int sound_element = SND_ELEMENT(element);
14256     int sound_action = ACTION_WALKING;
14257
14258     if (IS_RND_GATE(element))
14259     {
14260       if (!player->key[RND_GATE_NR(element)])
14261         return MP_NO_ACTION;
14262     }
14263     else if (IS_RND_GATE_GRAY(element))
14264     {
14265       if (!player->key[RND_GATE_GRAY_NR(element)])
14266         return MP_NO_ACTION;
14267     }
14268     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14269     {
14270       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14271         return MP_NO_ACTION;
14272     }
14273     else if (element == EL_EXIT_OPEN ||
14274              element == EL_EM_EXIT_OPEN ||
14275              element == EL_EM_EXIT_OPENING ||
14276              element == EL_STEEL_EXIT_OPEN ||
14277              element == EL_EM_STEEL_EXIT_OPEN ||
14278              element == EL_EM_STEEL_EXIT_OPENING ||
14279              element == EL_SP_EXIT_OPEN ||
14280              element == EL_SP_EXIT_OPENING)
14281     {
14282       sound_action = ACTION_PASSING;    // player is passing exit
14283     }
14284     else if (element == EL_EMPTY)
14285     {
14286       sound_action = ACTION_MOVING;             // nothing to walk on
14287     }
14288
14289     // play sound from background or player, whatever is available
14290     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14291       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14292     else
14293       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14294   }
14295   else if (player_can_move &&
14296            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14297   {
14298     if (!ACCESS_FROM(element, opposite_direction))
14299       return MP_NO_ACTION;      // field not accessible from this direction
14300
14301     if (CAN_MOVE(element))      // only fixed elements can be passed!
14302       return MP_NO_ACTION;
14303
14304     if (IS_EM_GATE(element))
14305     {
14306       if (!player->key[EM_GATE_NR(element)])
14307         return MP_NO_ACTION;
14308     }
14309     else if (IS_EM_GATE_GRAY(element))
14310     {
14311       if (!player->key[EM_GATE_GRAY_NR(element)])
14312         return MP_NO_ACTION;
14313     }
14314     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14315     {
14316       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14317         return MP_NO_ACTION;
14318     }
14319     else if (IS_EMC_GATE(element))
14320     {
14321       if (!player->key[EMC_GATE_NR(element)])
14322         return MP_NO_ACTION;
14323     }
14324     else if (IS_EMC_GATE_GRAY(element))
14325     {
14326       if (!player->key[EMC_GATE_GRAY_NR(element)])
14327         return MP_NO_ACTION;
14328     }
14329     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14330     {
14331       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14332         return MP_NO_ACTION;
14333     }
14334     else if (element == EL_DC_GATE_WHITE ||
14335              element == EL_DC_GATE_WHITE_GRAY ||
14336              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14337     {
14338       if (player->num_white_keys == 0)
14339         return MP_NO_ACTION;
14340
14341       player->num_white_keys--;
14342     }
14343     else if (IS_SP_PORT(element))
14344     {
14345       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14346           element == EL_SP_GRAVITY_PORT_RIGHT ||
14347           element == EL_SP_GRAVITY_PORT_UP ||
14348           element == EL_SP_GRAVITY_PORT_DOWN)
14349         player->gravity = !player->gravity;
14350       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14351                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14352                element == EL_SP_GRAVITY_ON_PORT_UP ||
14353                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14354         player->gravity = TRUE;
14355       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14356                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14357                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14358                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14359         player->gravity = FALSE;
14360     }
14361
14362     // automatically move to the next field with double speed
14363     player->programmed_action = move_direction;
14364
14365     if (player->move_delay_reset_counter == 0)
14366     {
14367       player->move_delay_reset_counter = 2;     // two double speed steps
14368
14369       DOUBLE_PLAYER_SPEED(player);
14370     }
14371
14372     PlayLevelSoundAction(x, y, ACTION_PASSING);
14373   }
14374   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14375   {
14376     RemoveField(x, y);
14377
14378     if (mode != DF_SNAP)
14379     {
14380       GfxElement[x][y] = GFX_ELEMENT(element);
14381       player->is_digging = TRUE;
14382     }
14383
14384     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14385
14386     // use old behaviour for old levels (digging)
14387     if (!level.finish_dig_collect)
14388     {
14389       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14390                                           player->index_bit, dig_side);
14391
14392       // if digging triggered player relocation, finish digging tile
14393       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14394         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14395     }
14396
14397     if (mode == DF_SNAP)
14398     {
14399       if (level.block_snap_field)
14400         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14401       else
14402         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14403
14404       // use old behaviour for old levels (snapping)
14405       if (!level.finish_dig_collect)
14406         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14407                                             player->index_bit, dig_side);
14408     }
14409   }
14410   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14411   {
14412     RemoveField(x, y);
14413
14414     if (is_player && mode != DF_SNAP)
14415     {
14416       GfxElement[x][y] = element;
14417       player->is_collecting = TRUE;
14418     }
14419
14420     if (element == EL_SPEED_PILL)
14421     {
14422       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14423     }
14424     else if (element == EL_EXTRA_TIME && level.time > 0)
14425     {
14426       TimeLeft += level.extra_time;
14427
14428       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14429
14430       DisplayGameControlValues();
14431     }
14432     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14433     {
14434       int shield_time = (element == EL_SHIELD_DEADLY ?
14435                          level.shield_deadly_time :
14436                          level.shield_normal_time);
14437
14438       player->shield_normal_time_left += shield_time;
14439       if (element == EL_SHIELD_DEADLY)
14440         player->shield_deadly_time_left += shield_time;
14441     }
14442     else if (element == EL_DYNAMITE ||
14443              element == EL_EM_DYNAMITE ||
14444              element == EL_SP_DISK_RED)
14445     {
14446       if (player->inventory_size < MAX_INVENTORY_SIZE)
14447         player->inventory_element[player->inventory_size++] = element;
14448
14449       DrawGameDoorValues();
14450     }
14451     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14452     {
14453       player->dynabomb_count++;
14454       player->dynabombs_left++;
14455     }
14456     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14457     {
14458       player->dynabomb_size++;
14459     }
14460     else if (element == EL_DYNABOMB_INCREASE_POWER)
14461     {
14462       player->dynabomb_xl = TRUE;
14463     }
14464     else if (IS_KEY(element))
14465     {
14466       player->key[KEY_NR(element)] = TRUE;
14467
14468       DrawGameDoorValues();
14469     }
14470     else if (element == EL_DC_KEY_WHITE)
14471     {
14472       player->num_white_keys++;
14473
14474       // display white keys?
14475       // DrawGameDoorValues();
14476     }
14477     else if (IS_ENVELOPE(element))
14478     {
14479       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14480
14481       if (!wait_for_snapping)
14482         player->show_envelope = element;
14483     }
14484     else if (element == EL_EMC_LENSES)
14485     {
14486       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14487
14488       RedrawAllInvisibleElementsForLenses();
14489     }
14490     else if (element == EL_EMC_MAGNIFIER)
14491     {
14492       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14493
14494       RedrawAllInvisibleElementsForMagnifier();
14495     }
14496     else if (IS_DROPPABLE(element) ||
14497              IS_THROWABLE(element))     // can be collected and dropped
14498     {
14499       int i;
14500
14501       if (collect_count == 0)
14502         player->inventory_infinite_element = element;
14503       else
14504         for (i = 0; i < collect_count; i++)
14505           if (player->inventory_size < MAX_INVENTORY_SIZE)
14506             player->inventory_element[player->inventory_size++] = element;
14507
14508       DrawGameDoorValues();
14509     }
14510     else if (collect_count > 0)
14511     {
14512       game.gems_still_needed -= collect_count;
14513       if (game.gems_still_needed < 0)
14514         game.gems_still_needed = 0;
14515
14516       game.snapshot.collected_item = TRUE;
14517
14518       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14519
14520       DisplayGameControlValues();
14521     }
14522
14523     RaiseScoreElement(element);
14524     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14525
14526     // use old behaviour for old levels (collecting)
14527     if (!level.finish_dig_collect && is_player)
14528     {
14529       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14530                                           player->index_bit, dig_side);
14531
14532       // if collecting triggered player relocation, finish collecting tile
14533       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14534         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14535     }
14536
14537     if (mode == DF_SNAP)
14538     {
14539       if (level.block_snap_field)
14540         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14541       else
14542         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14543
14544       // use old behaviour for old levels (snapping)
14545       if (!level.finish_dig_collect)
14546         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14547                                             player->index_bit, dig_side);
14548     }
14549   }
14550   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14551   {
14552     if (mode == DF_SNAP && element != EL_BD_ROCK)
14553       return MP_NO_ACTION;
14554
14555     if (CAN_FALL(element) && dy)
14556       return MP_NO_ACTION;
14557
14558     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14559         !(element == EL_SPRING && level.use_spring_bug))
14560       return MP_NO_ACTION;
14561
14562     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14563         ((move_direction & MV_VERTICAL &&
14564           ((element_info[element].move_pattern & MV_LEFT &&
14565             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14566            (element_info[element].move_pattern & MV_RIGHT &&
14567             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14568          (move_direction & MV_HORIZONTAL &&
14569           ((element_info[element].move_pattern & MV_UP &&
14570             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14571            (element_info[element].move_pattern & MV_DOWN &&
14572             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14573       return MP_NO_ACTION;
14574
14575     // do not push elements already moving away faster than player
14576     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14577         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14578       return MP_NO_ACTION;
14579
14580     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14581     {
14582       if (player->push_delay_value == -1 || !player_was_pushing)
14583         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14584     }
14585     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14586     {
14587       if (player->push_delay_value == -1)
14588         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14589     }
14590     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14591     {
14592       if (!player->is_pushing)
14593         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14594     }
14595
14596     player->is_pushing = TRUE;
14597     player->is_active = TRUE;
14598
14599     if (!(IN_LEV_FIELD(nextx, nexty) &&
14600           (IS_FREE(nextx, nexty) ||
14601            (IS_SB_ELEMENT(element) &&
14602             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14603            (IS_CUSTOM_ELEMENT(element) &&
14604             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14605       return MP_NO_ACTION;
14606
14607     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14608       return MP_NO_ACTION;
14609
14610     if (player->push_delay == -1)       // new pushing; restart delay
14611       player->push_delay = 0;
14612
14613     if (player->push_delay < player->push_delay_value &&
14614         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14615         element != EL_SPRING && element != EL_BALLOON)
14616     {
14617       // make sure that there is no move delay before next try to push
14618       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14619         player->move_delay = 0;
14620
14621       return MP_NO_ACTION;
14622     }
14623
14624     if (IS_CUSTOM_ELEMENT(element) &&
14625         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14626     {
14627       if (!DigFieldByCE(nextx, nexty, element))
14628         return MP_NO_ACTION;
14629     }
14630
14631     if (IS_SB_ELEMENT(element))
14632     {
14633       boolean sokoban_task_solved = FALSE;
14634
14635       if (element == EL_SOKOBAN_FIELD_FULL)
14636       {
14637         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14638
14639         IncrementSokobanFieldsNeeded();
14640         IncrementSokobanObjectsNeeded();
14641       }
14642
14643       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14644       {
14645         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14646
14647         DecrementSokobanFieldsNeeded();
14648         DecrementSokobanObjectsNeeded();
14649
14650         // sokoban object was pushed from empty field to sokoban field
14651         if (Back[x][y] == EL_EMPTY)
14652           sokoban_task_solved = TRUE;
14653       }
14654
14655       Tile[x][y] = EL_SOKOBAN_OBJECT;
14656
14657       if (Back[x][y] == Back[nextx][nexty])
14658         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14659       else if (Back[x][y] != 0)
14660         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14661                                     ACTION_EMPTYING);
14662       else
14663         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14664                                     ACTION_FILLING);
14665
14666       if (sokoban_task_solved &&
14667           game.sokoban_fields_still_needed == 0 &&
14668           game.sokoban_objects_still_needed == 0 &&
14669           level.auto_exit_sokoban)
14670       {
14671         game.players_still_needed = 0;
14672
14673         LevelSolved();
14674
14675         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14676       }
14677     }
14678     else
14679       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14680
14681     InitMovingField(x, y, move_direction);
14682     GfxAction[x][y] = ACTION_PUSHING;
14683
14684     if (mode == DF_SNAP)
14685       ContinueMoving(x, y);
14686     else
14687       MovPos[x][y] = (dx != 0 ? dx : dy);
14688
14689     Pushed[x][y] = TRUE;
14690     Pushed[nextx][nexty] = TRUE;
14691
14692     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14693       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14694     else
14695       player->push_delay_value = -1;    // get new value later
14696
14697     // check for element change _after_ element has been pushed
14698     if (game.use_change_when_pushing_bug)
14699     {
14700       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14701                                  player->index_bit, dig_side);
14702       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14703                                           player->index_bit, dig_side);
14704     }
14705   }
14706   else if (IS_SWITCHABLE(element))
14707   {
14708     if (PLAYER_SWITCHING(player, x, y))
14709     {
14710       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14711                                           player->index_bit, dig_side);
14712
14713       return MP_ACTION;
14714     }
14715
14716     player->is_switching = TRUE;
14717     player->switch_x = x;
14718     player->switch_y = y;
14719
14720     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14721
14722     if (element == EL_ROBOT_WHEEL)
14723     {
14724       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14725
14726       game.robot_wheel_x = x;
14727       game.robot_wheel_y = y;
14728       game.robot_wheel_active = TRUE;
14729
14730       TEST_DrawLevelField(x, y);
14731     }
14732     else if (element == EL_SP_TERMINAL)
14733     {
14734       int xx, yy;
14735
14736       SCAN_PLAYFIELD(xx, yy)
14737       {
14738         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14739         {
14740           Bang(xx, yy);
14741         }
14742         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14743         {
14744           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14745
14746           ResetGfxAnimation(xx, yy);
14747           TEST_DrawLevelField(xx, yy);
14748         }
14749       }
14750     }
14751     else if (IS_BELT_SWITCH(element))
14752     {
14753       ToggleBeltSwitch(x, y);
14754     }
14755     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14756              element == EL_SWITCHGATE_SWITCH_DOWN ||
14757              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14758              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14759     {
14760       ToggleSwitchgateSwitch(x, y);
14761     }
14762     else if (element == EL_LIGHT_SWITCH ||
14763              element == EL_LIGHT_SWITCH_ACTIVE)
14764     {
14765       ToggleLightSwitch(x, y);
14766     }
14767     else if (element == EL_TIMEGATE_SWITCH ||
14768              element == EL_DC_TIMEGATE_SWITCH)
14769     {
14770       ActivateTimegateSwitch(x, y);
14771     }
14772     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14773              element == EL_BALLOON_SWITCH_RIGHT ||
14774              element == EL_BALLOON_SWITCH_UP    ||
14775              element == EL_BALLOON_SWITCH_DOWN  ||
14776              element == EL_BALLOON_SWITCH_NONE  ||
14777              element == EL_BALLOON_SWITCH_ANY)
14778     {
14779       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14780                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14781                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14782                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14783                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14784                              move_direction);
14785     }
14786     else if (element == EL_LAMP)
14787     {
14788       Tile[x][y] = EL_LAMP_ACTIVE;
14789       game.lights_still_needed--;
14790
14791       ResetGfxAnimation(x, y);
14792       TEST_DrawLevelField(x, y);
14793     }
14794     else if (element == EL_TIME_ORB_FULL)
14795     {
14796       Tile[x][y] = EL_TIME_ORB_EMPTY;
14797
14798       if (level.time > 0 || level.use_time_orb_bug)
14799       {
14800         TimeLeft += level.time_orb_time;
14801         game.no_level_time_limit = FALSE;
14802
14803         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14804
14805         DisplayGameControlValues();
14806       }
14807
14808       ResetGfxAnimation(x, y);
14809       TEST_DrawLevelField(x, y);
14810     }
14811     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14812              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14813     {
14814       int xx, yy;
14815
14816       game.ball_active = !game.ball_active;
14817
14818       SCAN_PLAYFIELD(xx, yy)
14819       {
14820         int e = Tile[xx][yy];
14821
14822         if (game.ball_active)
14823         {
14824           if (e == EL_EMC_MAGIC_BALL)
14825             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14826           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14827             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14828         }
14829         else
14830         {
14831           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14832             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14833           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14834             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14835         }
14836       }
14837     }
14838
14839     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14840                                         player->index_bit, dig_side);
14841
14842     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14843                                         player->index_bit, dig_side);
14844
14845     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14846                                         player->index_bit, dig_side);
14847
14848     return MP_ACTION;
14849   }
14850   else
14851   {
14852     if (!PLAYER_SWITCHING(player, x, y))
14853     {
14854       player->is_switching = TRUE;
14855       player->switch_x = x;
14856       player->switch_y = y;
14857
14858       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14859                                  player->index_bit, dig_side);
14860       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14861                                           player->index_bit, dig_side);
14862
14863       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14864                                  player->index_bit, dig_side);
14865       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14866                                           player->index_bit, dig_side);
14867     }
14868
14869     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14870                                player->index_bit, dig_side);
14871     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14872                                         player->index_bit, dig_side);
14873
14874     return MP_NO_ACTION;
14875   }
14876
14877   player->push_delay = -1;
14878
14879   if (is_player)                // function can also be called by EL_PENGUIN
14880   {
14881     if (Tile[x][y] != element)          // really digged/collected something
14882     {
14883       player->is_collecting = !player->is_digging;
14884       player->is_active = TRUE;
14885
14886       player->last_removed_element = element;
14887     }
14888   }
14889
14890   return MP_MOVING;
14891 }
14892
14893 static boolean DigFieldByCE(int x, int y, int digging_element)
14894 {
14895   int element = Tile[x][y];
14896
14897   if (!IS_FREE(x, y))
14898   {
14899     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14900                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14901                   ACTION_BREAKING);
14902
14903     // no element can dig solid indestructible elements
14904     if (IS_INDESTRUCTIBLE(element) &&
14905         !IS_DIGGABLE(element) &&
14906         !IS_COLLECTIBLE(element))
14907       return FALSE;
14908
14909     if (AmoebaNr[x][y] &&
14910         (element == EL_AMOEBA_FULL ||
14911          element == EL_BD_AMOEBA ||
14912          element == EL_AMOEBA_GROWING))
14913     {
14914       AmoebaCnt[AmoebaNr[x][y]]--;
14915       AmoebaCnt2[AmoebaNr[x][y]]--;
14916     }
14917
14918     if (IS_MOVING(x, y))
14919       RemoveMovingField(x, y);
14920     else
14921     {
14922       RemoveField(x, y);
14923       TEST_DrawLevelField(x, y);
14924     }
14925
14926     // if digged element was about to explode, prevent the explosion
14927     ExplodeField[x][y] = EX_TYPE_NONE;
14928
14929     PlayLevelSoundAction(x, y, action);
14930   }
14931
14932   Store[x][y] = EL_EMPTY;
14933
14934   // this makes it possible to leave the removed element again
14935   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14936     Store[x][y] = element;
14937
14938   return TRUE;
14939 }
14940
14941 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14942 {
14943   int jx = player->jx, jy = player->jy;
14944   int x = jx + dx, y = jy + dy;
14945   int snap_direction = (dx == -1 ? MV_LEFT  :
14946                         dx == +1 ? MV_RIGHT :
14947                         dy == -1 ? MV_UP    :
14948                         dy == +1 ? MV_DOWN  : MV_NONE);
14949   boolean can_continue_snapping = (level.continuous_snapping &&
14950                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14951
14952   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14953     return FALSE;
14954
14955   if (!player->active || !IN_LEV_FIELD(x, y))
14956     return FALSE;
14957
14958   if (dx && dy)
14959     return FALSE;
14960
14961   if (!dx && !dy)
14962   {
14963     if (player->MovPos == 0)
14964       player->is_pushing = FALSE;
14965
14966     player->is_snapping = FALSE;
14967
14968     if (player->MovPos == 0)
14969     {
14970       player->is_moving = FALSE;
14971       player->is_digging = FALSE;
14972       player->is_collecting = FALSE;
14973     }
14974
14975     return FALSE;
14976   }
14977
14978   // prevent snapping with already pressed snap key when not allowed
14979   if (player->is_snapping && !can_continue_snapping)
14980     return FALSE;
14981
14982   player->MovDir = snap_direction;
14983
14984   if (player->MovPos == 0)
14985   {
14986     player->is_moving = FALSE;
14987     player->is_digging = FALSE;
14988     player->is_collecting = FALSE;
14989   }
14990
14991   player->is_dropping = FALSE;
14992   player->is_dropping_pressed = FALSE;
14993   player->drop_pressed_delay = 0;
14994
14995   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14996     return FALSE;
14997
14998   player->is_snapping = TRUE;
14999   player->is_active = TRUE;
15000
15001   if (player->MovPos == 0)
15002   {
15003     player->is_moving = FALSE;
15004     player->is_digging = FALSE;
15005     player->is_collecting = FALSE;
15006   }
15007
15008   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15009     TEST_DrawLevelField(player->last_jx, player->last_jy);
15010
15011   TEST_DrawLevelField(x, y);
15012
15013   return TRUE;
15014 }
15015
15016 static boolean DropElement(struct PlayerInfo *player)
15017 {
15018   int old_element, new_element;
15019   int dropx = player->jx, dropy = player->jy;
15020   int drop_direction = player->MovDir;
15021   int drop_side = drop_direction;
15022   int drop_element = get_next_dropped_element(player);
15023
15024   /* do not drop an element on top of another element; when holding drop key
15025      pressed without moving, dropped element must move away before the next
15026      element can be dropped (this is especially important if the next element
15027      is dynamite, which can be placed on background for historical reasons) */
15028   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15029     return MP_ACTION;
15030
15031   if (IS_THROWABLE(drop_element))
15032   {
15033     dropx += GET_DX_FROM_DIR(drop_direction);
15034     dropy += GET_DY_FROM_DIR(drop_direction);
15035
15036     if (!IN_LEV_FIELD(dropx, dropy))
15037       return FALSE;
15038   }
15039
15040   old_element = Tile[dropx][dropy];     // old element at dropping position
15041   new_element = drop_element;           // default: no change when dropping
15042
15043   // check if player is active, not moving and ready to drop
15044   if (!player->active || player->MovPos || player->drop_delay > 0)
15045     return FALSE;
15046
15047   // check if player has anything that can be dropped
15048   if (new_element == EL_UNDEFINED)
15049     return FALSE;
15050
15051   // only set if player has anything that can be dropped
15052   player->is_dropping_pressed = TRUE;
15053
15054   // check if drop key was pressed long enough for EM style dynamite
15055   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15056     return FALSE;
15057
15058   // check if anything can be dropped at the current position
15059   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15060     return FALSE;
15061
15062   // collected custom elements can only be dropped on empty fields
15063   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15064     return FALSE;
15065
15066   if (old_element != EL_EMPTY)
15067     Back[dropx][dropy] = old_element;   // store old element on this field
15068
15069   ResetGfxAnimation(dropx, dropy);
15070   ResetRandomAnimationValue(dropx, dropy);
15071
15072   if (player->inventory_size > 0 ||
15073       player->inventory_infinite_element != EL_UNDEFINED)
15074   {
15075     if (player->inventory_size > 0)
15076     {
15077       player->inventory_size--;
15078
15079       DrawGameDoorValues();
15080
15081       if (new_element == EL_DYNAMITE)
15082         new_element = EL_DYNAMITE_ACTIVE;
15083       else if (new_element == EL_EM_DYNAMITE)
15084         new_element = EL_EM_DYNAMITE_ACTIVE;
15085       else if (new_element == EL_SP_DISK_RED)
15086         new_element = EL_SP_DISK_RED_ACTIVE;
15087     }
15088
15089     Tile[dropx][dropy] = new_element;
15090
15091     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15092       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15093                           el2img(Tile[dropx][dropy]), 0);
15094
15095     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15096
15097     // needed if previous element just changed to "empty" in the last frame
15098     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15099
15100     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15101                                player->index_bit, drop_side);
15102     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15103                                         CE_PLAYER_DROPS_X,
15104                                         player->index_bit, drop_side);
15105
15106     TestIfElementTouchesCustomElement(dropx, dropy);
15107   }
15108   else          // player is dropping a dyna bomb
15109   {
15110     player->dynabombs_left--;
15111
15112     Tile[dropx][dropy] = new_element;
15113
15114     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15115       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15116                           el2img(Tile[dropx][dropy]), 0);
15117
15118     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15119   }
15120
15121   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15122     InitField_WithBug1(dropx, dropy, FALSE);
15123
15124   new_element = Tile[dropx][dropy];     // element might have changed
15125
15126   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15127       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15128   {
15129     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15130       MovDir[dropx][dropy] = drop_direction;
15131
15132     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15133
15134     // do not cause impact style collision by dropping elements that can fall
15135     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15136   }
15137
15138   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15139   player->is_dropping = TRUE;
15140
15141   player->drop_pressed_delay = 0;
15142   player->is_dropping_pressed = FALSE;
15143
15144   player->drop_x = dropx;
15145   player->drop_y = dropy;
15146
15147   return TRUE;
15148 }
15149
15150 // ----------------------------------------------------------------------------
15151 // game sound playing functions
15152 // ----------------------------------------------------------------------------
15153
15154 static int *loop_sound_frame = NULL;
15155 static int *loop_sound_volume = NULL;
15156
15157 void InitPlayLevelSound(void)
15158 {
15159   int num_sounds = getSoundListSize();
15160
15161   checked_free(loop_sound_frame);
15162   checked_free(loop_sound_volume);
15163
15164   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15165   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15166 }
15167
15168 static void PlayLevelSound(int x, int y, int nr)
15169 {
15170   int sx = SCREENX(x), sy = SCREENY(y);
15171   int volume, stereo_position;
15172   int max_distance = 8;
15173   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15174
15175   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15176       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15177     return;
15178
15179   if (!IN_LEV_FIELD(x, y) ||
15180       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15181       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15182     return;
15183
15184   volume = SOUND_MAX_VOLUME;
15185
15186   if (!IN_SCR_FIELD(sx, sy))
15187   {
15188     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15189     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15190
15191     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15192   }
15193
15194   stereo_position = (SOUND_MAX_LEFT +
15195                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15196                      (SCR_FIELDX + 2 * max_distance));
15197
15198   if (IS_LOOP_SOUND(nr))
15199   {
15200     /* This assures that quieter loop sounds do not overwrite louder ones,
15201        while restarting sound volume comparison with each new game frame. */
15202
15203     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15204       return;
15205
15206     loop_sound_volume[nr] = volume;
15207     loop_sound_frame[nr] = FrameCounter;
15208   }
15209
15210   PlaySoundExt(nr, volume, stereo_position, type);
15211 }
15212
15213 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15214 {
15215   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15216                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15217                  y < LEVELY(BY1) ? LEVELY(BY1) :
15218                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15219                  sound_action);
15220 }
15221
15222 static void PlayLevelSoundAction(int x, int y, int action)
15223 {
15224   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15225 }
15226
15227 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15228 {
15229   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15230
15231   if (sound_effect != SND_UNDEFINED)
15232     PlayLevelSound(x, y, sound_effect);
15233 }
15234
15235 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15236                                               int action)
15237 {
15238   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15239
15240   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15241     PlayLevelSound(x, y, sound_effect);
15242 }
15243
15244 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15245 {
15246   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15247
15248   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15249     PlayLevelSound(x, y, sound_effect);
15250 }
15251
15252 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15253 {
15254   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15255
15256   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15257     StopSound(sound_effect);
15258 }
15259
15260 static int getLevelMusicNr(void)
15261 {
15262   if (levelset.music[level_nr] != MUS_UNDEFINED)
15263     return levelset.music[level_nr];            // from config file
15264   else
15265     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15266 }
15267
15268 static void FadeLevelSounds(void)
15269 {
15270   FadeSounds();
15271 }
15272
15273 static void FadeLevelMusic(void)
15274 {
15275   int music_nr = getLevelMusicNr();
15276   char *curr_music = getCurrentlyPlayingMusicFilename();
15277   char *next_music = getMusicInfoEntryFilename(music_nr);
15278
15279   if (!strEqual(curr_music, next_music))
15280     FadeMusic();
15281 }
15282
15283 void FadeLevelSoundsAndMusic(void)
15284 {
15285   FadeLevelSounds();
15286   FadeLevelMusic();
15287 }
15288
15289 static void PlayLevelMusic(void)
15290 {
15291   int music_nr = getLevelMusicNr();
15292   char *curr_music = getCurrentlyPlayingMusicFilename();
15293   char *next_music = getMusicInfoEntryFilename(music_nr);
15294
15295   if (!strEqual(curr_music, next_music))
15296     PlayMusicLoop(music_nr);
15297 }
15298
15299 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15300 {
15301   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15302   int offset = 0;
15303   int x = xx - offset;
15304   int y = yy - offset;
15305
15306   switch (sample)
15307   {
15308     case SOUND_blank:
15309       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15310       break;
15311
15312     case SOUND_roll:
15313       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15314       break;
15315
15316     case SOUND_stone:
15317       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15318       break;
15319
15320     case SOUND_nut:
15321       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15322       break;
15323
15324     case SOUND_crack:
15325       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15326       break;
15327
15328     case SOUND_bug:
15329       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15330       break;
15331
15332     case SOUND_tank:
15333       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15334       break;
15335
15336     case SOUND_android_clone:
15337       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15338       break;
15339
15340     case SOUND_android_move:
15341       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15342       break;
15343
15344     case SOUND_spring:
15345       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15346       break;
15347
15348     case SOUND_slurp:
15349       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15350       break;
15351
15352     case SOUND_eater:
15353       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15354       break;
15355
15356     case SOUND_eater_eat:
15357       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15358       break;
15359
15360     case SOUND_alien:
15361       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15362       break;
15363
15364     case SOUND_collect:
15365       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15366       break;
15367
15368     case SOUND_diamond:
15369       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15370       break;
15371
15372     case SOUND_squash:
15373       // !!! CHECK THIS !!!
15374 #if 1
15375       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15376 #else
15377       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15378 #endif
15379       break;
15380
15381     case SOUND_wonderfall:
15382       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15383       break;
15384
15385     case SOUND_drip:
15386       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15387       break;
15388
15389     case SOUND_push:
15390       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15391       break;
15392
15393     case SOUND_dirt:
15394       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15395       break;
15396
15397     case SOUND_acid:
15398       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15399       break;
15400
15401     case SOUND_ball:
15402       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15403       break;
15404
15405     case SOUND_slide:
15406       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15407       break;
15408
15409     case SOUND_wonder:
15410       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15411       break;
15412
15413     case SOUND_door:
15414       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15415       break;
15416
15417     case SOUND_exit_open:
15418       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15419       break;
15420
15421     case SOUND_exit_leave:
15422       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15423       break;
15424
15425     case SOUND_dynamite:
15426       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15427       break;
15428
15429     case SOUND_tick:
15430       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15431       break;
15432
15433     case SOUND_press:
15434       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15435       break;
15436
15437     case SOUND_wheel:
15438       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15439       break;
15440
15441     case SOUND_boom:
15442       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15443       break;
15444
15445     case SOUND_die:
15446       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15447       break;
15448
15449     case SOUND_time:
15450       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15451       break;
15452
15453     default:
15454       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15455       break;
15456   }
15457 }
15458
15459 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15460 {
15461   int element = map_element_SP_to_RND(element_sp);
15462   int action = map_action_SP_to_RND(action_sp);
15463   int offset = (setup.sp_show_border_elements ? 0 : 1);
15464   int x = xx - offset;
15465   int y = yy - offset;
15466
15467   PlayLevelSoundElementAction(x, y, element, action);
15468 }
15469
15470 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15471 {
15472   int element = map_element_MM_to_RND(element_mm);
15473   int action = map_action_MM_to_RND(action_mm);
15474   int offset = 0;
15475   int x = xx - offset;
15476   int y = yy - offset;
15477
15478   if (!IS_MM_ELEMENT(element))
15479     element = EL_MM_DEFAULT;
15480
15481   PlayLevelSoundElementAction(x, y, element, action);
15482 }
15483
15484 void PlaySound_MM(int sound_mm)
15485 {
15486   int sound = map_sound_MM_to_RND(sound_mm);
15487
15488   if (sound == SND_UNDEFINED)
15489     return;
15490
15491   PlaySound(sound);
15492 }
15493
15494 void PlaySoundLoop_MM(int sound_mm)
15495 {
15496   int sound = map_sound_MM_to_RND(sound_mm);
15497
15498   if (sound == SND_UNDEFINED)
15499     return;
15500
15501   PlaySoundLoop(sound);
15502 }
15503
15504 void StopSound_MM(int sound_mm)
15505 {
15506   int sound = map_sound_MM_to_RND(sound_mm);
15507
15508   if (sound == SND_UNDEFINED)
15509     return;
15510
15511   StopSound(sound);
15512 }
15513
15514 void RaiseScore(int value)
15515 {
15516   game.score += value;
15517
15518   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15519
15520   DisplayGameControlValues();
15521 }
15522
15523 void RaiseScoreElement(int element)
15524 {
15525   switch (element)
15526   {
15527     case EL_EMERALD:
15528     case EL_BD_DIAMOND:
15529     case EL_EMERALD_YELLOW:
15530     case EL_EMERALD_RED:
15531     case EL_EMERALD_PURPLE:
15532     case EL_SP_INFOTRON:
15533       RaiseScore(level.score[SC_EMERALD]);
15534       break;
15535     case EL_DIAMOND:
15536       RaiseScore(level.score[SC_DIAMOND]);
15537       break;
15538     case EL_CRYSTAL:
15539       RaiseScore(level.score[SC_CRYSTAL]);
15540       break;
15541     case EL_PEARL:
15542       RaiseScore(level.score[SC_PEARL]);
15543       break;
15544     case EL_BUG:
15545     case EL_BD_BUTTERFLY:
15546     case EL_SP_ELECTRON:
15547       RaiseScore(level.score[SC_BUG]);
15548       break;
15549     case EL_SPACESHIP:
15550     case EL_BD_FIREFLY:
15551     case EL_SP_SNIKSNAK:
15552       RaiseScore(level.score[SC_SPACESHIP]);
15553       break;
15554     case EL_YAMYAM:
15555     case EL_DARK_YAMYAM:
15556       RaiseScore(level.score[SC_YAMYAM]);
15557       break;
15558     case EL_ROBOT:
15559       RaiseScore(level.score[SC_ROBOT]);
15560       break;
15561     case EL_PACMAN:
15562       RaiseScore(level.score[SC_PACMAN]);
15563       break;
15564     case EL_NUT:
15565       RaiseScore(level.score[SC_NUT]);
15566       break;
15567     case EL_DYNAMITE:
15568     case EL_EM_DYNAMITE:
15569     case EL_SP_DISK_RED:
15570     case EL_DYNABOMB_INCREASE_NUMBER:
15571     case EL_DYNABOMB_INCREASE_SIZE:
15572     case EL_DYNABOMB_INCREASE_POWER:
15573       RaiseScore(level.score[SC_DYNAMITE]);
15574       break;
15575     case EL_SHIELD_NORMAL:
15576     case EL_SHIELD_DEADLY:
15577       RaiseScore(level.score[SC_SHIELD]);
15578       break;
15579     case EL_EXTRA_TIME:
15580       RaiseScore(level.extra_time_score);
15581       break;
15582     case EL_KEY_1:
15583     case EL_KEY_2:
15584     case EL_KEY_3:
15585     case EL_KEY_4:
15586     case EL_EM_KEY_1:
15587     case EL_EM_KEY_2:
15588     case EL_EM_KEY_3:
15589     case EL_EM_KEY_4:
15590     case EL_EMC_KEY_5:
15591     case EL_EMC_KEY_6:
15592     case EL_EMC_KEY_7:
15593     case EL_EMC_KEY_8:
15594     case EL_DC_KEY_WHITE:
15595       RaiseScore(level.score[SC_KEY]);
15596       break;
15597     default:
15598       RaiseScore(element_info[element].collect_score);
15599       break;
15600   }
15601 }
15602
15603 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15604 {
15605   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15606   {
15607     if (!quick_quit)
15608     {
15609       // prevent short reactivation of overlay buttons while closing door
15610       SetOverlayActive(FALSE);
15611       UnmapGameButtons();
15612
15613       // door may still be open due to skipped or envelope style request
15614       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15615     }
15616
15617     if (network.enabled)
15618       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15619     else
15620     {
15621       if (quick_quit)
15622         FadeSkipNextFadeIn();
15623
15624       SetGameStatus(GAME_MODE_MAIN);
15625
15626       DrawMainMenu();
15627     }
15628   }
15629   else          // continue playing the game
15630   {
15631     if (tape.playing && tape.deactivate_display)
15632       TapeDeactivateDisplayOff(TRUE);
15633
15634     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15635
15636     if (tape.playing && tape.deactivate_display)
15637       TapeDeactivateDisplayOn();
15638   }
15639 }
15640
15641 void RequestQuitGame(boolean escape_key_pressed)
15642 {
15643   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15644   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15645                         level_editor_test_game);
15646   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15647                           quick_quit || score_info_tape_play);
15648
15649   RequestQuitGameExt(skip_request, quick_quit,
15650                      "Do you really want to quit the game?");
15651 }
15652
15653 void RequestRestartGame(char *message)
15654 {
15655   game.restart_game_message = NULL;
15656
15657   boolean has_started_game = hasStartedNetworkGame();
15658   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15659
15660   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15661   {
15662     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15663   }
15664   else
15665   {
15666     // needed in case of envelope request to close game panel
15667     CloseDoor(DOOR_CLOSE_1);
15668
15669     SetGameStatus(GAME_MODE_MAIN);
15670
15671     DrawMainMenu();
15672   }
15673 }
15674
15675 void CheckGameOver(void)
15676 {
15677   static boolean last_game_over = FALSE;
15678   static int game_over_delay = 0;
15679   int game_over_delay_value = 50;
15680   boolean game_over = checkGameFailed();
15681
15682   // do not handle game over if request dialog is already active
15683   if (game.request_active)
15684     return;
15685
15686   // do not ask to play again if game was never actually played
15687   if (!game.GamePlayed)
15688     return;
15689
15690   if (!game_over)
15691   {
15692     last_game_over = FALSE;
15693     game_over_delay = game_over_delay_value;
15694
15695     return;
15696   }
15697
15698   if (game_over_delay > 0)
15699   {
15700     game_over_delay--;
15701
15702     return;
15703   }
15704
15705   if (last_game_over != game_over)
15706     game.restart_game_message = (hasStartedNetworkGame() ?
15707                                  "Game over! Play it again?" :
15708                                  "Game over!");
15709
15710   last_game_over = game_over;
15711 }
15712
15713 boolean checkGameSolved(void)
15714 {
15715   // set for all game engines if level was solved
15716   return game.LevelSolved_GameEnd;
15717 }
15718
15719 boolean checkGameFailed(void)
15720 {
15721   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15722     return (game_em.game_over && !game_em.level_solved);
15723   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15724     return (game_sp.game_over && !game_sp.level_solved);
15725   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15726     return (game_mm.game_over && !game_mm.level_solved);
15727   else                          // GAME_ENGINE_TYPE_RND
15728     return (game.GameOver && !game.LevelSolved);
15729 }
15730
15731 boolean checkGameEnded(void)
15732 {
15733   return (checkGameSolved() || checkGameFailed());
15734 }
15735
15736
15737 // ----------------------------------------------------------------------------
15738 // random generator functions
15739 // ----------------------------------------------------------------------------
15740
15741 unsigned int InitEngineRandom_RND(int seed)
15742 {
15743   game.num_random_calls = 0;
15744
15745   return InitEngineRandom(seed);
15746 }
15747
15748 unsigned int RND(int max)
15749 {
15750   if (max > 0)
15751   {
15752     game.num_random_calls++;
15753
15754     return GetEngineRandom(max);
15755   }
15756
15757   return 0;
15758 }
15759
15760
15761 // ----------------------------------------------------------------------------
15762 // game engine snapshot handling functions
15763 // ----------------------------------------------------------------------------
15764
15765 struct EngineSnapshotInfo
15766 {
15767   // runtime values for custom element collect score
15768   int collect_score[NUM_CUSTOM_ELEMENTS];
15769
15770   // runtime values for group element choice position
15771   int choice_pos[NUM_GROUP_ELEMENTS];
15772
15773   // runtime values for belt position animations
15774   int belt_graphic[4][NUM_BELT_PARTS];
15775   int belt_anim_mode[4][NUM_BELT_PARTS];
15776 };
15777
15778 static struct EngineSnapshotInfo engine_snapshot_rnd;
15779 static char *snapshot_level_identifier = NULL;
15780 static int snapshot_level_nr = -1;
15781
15782 static void SaveEngineSnapshotValues_RND(void)
15783 {
15784   static int belt_base_active_element[4] =
15785   {
15786     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15787     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15788     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15789     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15790   };
15791   int i, j;
15792
15793   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15794   {
15795     int element = EL_CUSTOM_START + i;
15796
15797     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15798   }
15799
15800   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15801   {
15802     int element = EL_GROUP_START + i;
15803
15804     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15805   }
15806
15807   for (i = 0; i < 4; i++)
15808   {
15809     for (j = 0; j < NUM_BELT_PARTS; j++)
15810     {
15811       int element = belt_base_active_element[i] + j;
15812       int graphic = el2img(element);
15813       int anim_mode = graphic_info[graphic].anim_mode;
15814
15815       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15816       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15817     }
15818   }
15819 }
15820
15821 static void LoadEngineSnapshotValues_RND(void)
15822 {
15823   unsigned int num_random_calls = game.num_random_calls;
15824   int i, j;
15825
15826   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15827   {
15828     int element = EL_CUSTOM_START + i;
15829
15830     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15831   }
15832
15833   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15834   {
15835     int element = EL_GROUP_START + i;
15836
15837     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15838   }
15839
15840   for (i = 0; i < 4; i++)
15841   {
15842     for (j = 0; j < NUM_BELT_PARTS; j++)
15843     {
15844       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15845       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15846
15847       graphic_info[graphic].anim_mode = anim_mode;
15848     }
15849   }
15850
15851   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15852   {
15853     InitRND(tape.random_seed);
15854     for (i = 0; i < num_random_calls; i++)
15855       RND(1);
15856   }
15857
15858   if (game.num_random_calls != num_random_calls)
15859   {
15860     Error("number of random calls out of sync");
15861     Error("number of random calls should be %d", num_random_calls);
15862     Error("number of random calls is %d", game.num_random_calls);
15863
15864     Fail("this should not happen -- please debug");
15865   }
15866 }
15867
15868 void FreeEngineSnapshotSingle(void)
15869 {
15870   FreeSnapshotSingle();
15871
15872   setString(&snapshot_level_identifier, NULL);
15873   snapshot_level_nr = -1;
15874 }
15875
15876 void FreeEngineSnapshotList(void)
15877 {
15878   FreeSnapshotList();
15879 }
15880
15881 static ListNode *SaveEngineSnapshotBuffers(void)
15882 {
15883   ListNode *buffers = NULL;
15884
15885   // copy some special values to a structure better suited for the snapshot
15886
15887   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15888     SaveEngineSnapshotValues_RND();
15889   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15890     SaveEngineSnapshotValues_EM();
15891   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15892     SaveEngineSnapshotValues_SP(&buffers);
15893   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15894     SaveEngineSnapshotValues_MM(&buffers);
15895
15896   // save values stored in special snapshot structure
15897
15898   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15899     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15900   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15901     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15902   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15903     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15904   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15905     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15906
15907   // save further RND engine values
15908
15909   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15910   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15911   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15912
15913   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15914   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15915   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15916   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15917   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15918
15919   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15920   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15921   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15922
15923   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15924
15925   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15926   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15927
15928   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15929   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15930   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15931   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15932   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15933   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15934   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15935   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15936   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15937   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15938   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15939   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15940   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15941   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15942   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15943   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15944   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15945   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15946
15947   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15948   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15949
15950   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15951   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15952   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15953
15954   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15955   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15956
15957   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15958   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15959   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
15960   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15961   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15962   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15963
15964   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15965   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15966
15967 #if 0
15968   ListNode *node = engine_snapshot_list_rnd;
15969   int num_bytes = 0;
15970
15971   while (node != NULL)
15972   {
15973     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15974
15975     node = node->next;
15976   }
15977
15978   Debug("game:playing:SaveEngineSnapshotBuffers",
15979         "size of engine snapshot: %d bytes", num_bytes);
15980 #endif
15981
15982   return buffers;
15983 }
15984
15985 void SaveEngineSnapshotSingle(void)
15986 {
15987   ListNode *buffers = SaveEngineSnapshotBuffers();
15988
15989   // finally save all snapshot buffers to single snapshot
15990   SaveSnapshotSingle(buffers);
15991
15992   // save level identification information
15993   setString(&snapshot_level_identifier, leveldir_current->identifier);
15994   snapshot_level_nr = level_nr;
15995 }
15996
15997 boolean CheckSaveEngineSnapshotToList(void)
15998 {
15999   boolean save_snapshot =
16000     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16001      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16002       game.snapshot.changed_action) ||
16003      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16004       game.snapshot.collected_item));
16005
16006   game.snapshot.changed_action = FALSE;
16007   game.snapshot.collected_item = FALSE;
16008   game.snapshot.save_snapshot = save_snapshot;
16009
16010   return save_snapshot;
16011 }
16012
16013 void SaveEngineSnapshotToList(void)
16014 {
16015   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16016       tape.quick_resume)
16017     return;
16018
16019   ListNode *buffers = SaveEngineSnapshotBuffers();
16020
16021   // finally save all snapshot buffers to snapshot list
16022   SaveSnapshotToList(buffers);
16023 }
16024
16025 void SaveEngineSnapshotToListInitial(void)
16026 {
16027   FreeEngineSnapshotList();
16028
16029   SaveEngineSnapshotToList();
16030 }
16031
16032 static void LoadEngineSnapshotValues(void)
16033 {
16034   // restore special values from snapshot structure
16035
16036   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16037     LoadEngineSnapshotValues_RND();
16038   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16039     LoadEngineSnapshotValues_EM();
16040   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16041     LoadEngineSnapshotValues_SP();
16042   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16043     LoadEngineSnapshotValues_MM();
16044 }
16045
16046 void LoadEngineSnapshotSingle(void)
16047 {
16048   LoadSnapshotSingle();
16049
16050   LoadEngineSnapshotValues();
16051 }
16052
16053 static void LoadEngineSnapshot_Undo(int steps)
16054 {
16055   LoadSnapshotFromList_Older(steps);
16056
16057   LoadEngineSnapshotValues();
16058 }
16059
16060 static void LoadEngineSnapshot_Redo(int steps)
16061 {
16062   LoadSnapshotFromList_Newer(steps);
16063
16064   LoadEngineSnapshotValues();
16065 }
16066
16067 boolean CheckEngineSnapshotSingle(void)
16068 {
16069   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16070           snapshot_level_nr == level_nr);
16071 }
16072
16073 boolean CheckEngineSnapshotList(void)
16074 {
16075   return CheckSnapshotList();
16076 }
16077
16078
16079 // ---------- new game button stuff -------------------------------------------
16080
16081 static struct
16082 {
16083   int graphic;
16084   struct XY *pos;
16085   int gadget_id;
16086   boolean *setup_value;
16087   boolean allowed_on_tape;
16088   boolean is_touch_button;
16089   char *infotext;
16090 } gamebutton_info[NUM_GAME_BUTTONS] =
16091 {
16092   {
16093     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16094     GAME_CTRL_ID_STOP,                          NULL,
16095     TRUE, FALSE,                                "stop game"
16096   },
16097   {
16098     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16099     GAME_CTRL_ID_PAUSE,                         NULL,
16100     TRUE, FALSE,                                "pause game"
16101   },
16102   {
16103     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16104     GAME_CTRL_ID_PLAY,                          NULL,
16105     TRUE, FALSE,                                "play game"
16106   },
16107   {
16108     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16109     GAME_CTRL_ID_UNDO,                          NULL,
16110     TRUE, FALSE,                                "undo step"
16111   },
16112   {
16113     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16114     GAME_CTRL_ID_REDO,                          NULL,
16115     TRUE, FALSE,                                "redo step"
16116   },
16117   {
16118     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16119     GAME_CTRL_ID_SAVE,                          NULL,
16120     TRUE, FALSE,                                "save game"
16121   },
16122   {
16123     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16124     GAME_CTRL_ID_PAUSE2,                        NULL,
16125     TRUE, FALSE,                                "pause game"
16126   },
16127   {
16128     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16129     GAME_CTRL_ID_LOAD,                          NULL,
16130     TRUE, FALSE,                                "load game"
16131   },
16132   {
16133     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16134     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16135     FALSE, FALSE,                               "stop game"
16136   },
16137   {
16138     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16139     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16140     FALSE, FALSE,                               "pause game"
16141   },
16142   {
16143     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16144     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16145     FALSE, FALSE,                               "play game"
16146   },
16147   {
16148     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16149     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16150     FALSE, TRUE,                                "stop game"
16151   },
16152   {
16153     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16154     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16155     FALSE, TRUE,                                "pause game"
16156   },
16157   {
16158     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16159     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16160     TRUE, FALSE,                                "background music on/off"
16161   },
16162   {
16163     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16164     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16165     TRUE, FALSE,                                "sound loops on/off"
16166   },
16167   {
16168     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16169     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16170     TRUE, FALSE,                                "normal sounds on/off"
16171   },
16172   {
16173     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16174     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16175     FALSE, FALSE,                               "background music on/off"
16176   },
16177   {
16178     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16179     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16180     FALSE, FALSE,                               "sound loops on/off"
16181   },
16182   {
16183     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16184     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16185     FALSE, FALSE,                               "normal sounds on/off"
16186   }
16187 };
16188
16189 void CreateGameButtons(void)
16190 {
16191   int i;
16192
16193   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16194   {
16195     int graphic = gamebutton_info[i].graphic;
16196     struct GraphicInfo *gfx = &graphic_info[graphic];
16197     struct XY *pos = gamebutton_info[i].pos;
16198     struct GadgetInfo *gi;
16199     int button_type;
16200     boolean checked;
16201     unsigned int event_mask;
16202     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16203     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16204     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16205     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16206     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16207     int gd_x   = gfx->src_x;
16208     int gd_y   = gfx->src_y;
16209     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16210     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16211     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16212     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16213     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16214     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16215     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16216     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16217     int id = i;
16218
16219     // do not use touch buttons if overlay touch buttons are disabled
16220     if (is_touch_button && !setup.touch.overlay_buttons)
16221       continue;
16222
16223     if (gfx->bitmap == NULL)
16224     {
16225       game_gadget[id] = NULL;
16226
16227       continue;
16228     }
16229
16230     if (id == GAME_CTRL_ID_STOP ||
16231         id == GAME_CTRL_ID_PANEL_STOP ||
16232         id == GAME_CTRL_ID_TOUCH_STOP ||
16233         id == GAME_CTRL_ID_PLAY ||
16234         id == GAME_CTRL_ID_PANEL_PLAY ||
16235         id == GAME_CTRL_ID_SAVE ||
16236         id == GAME_CTRL_ID_LOAD)
16237     {
16238       button_type = GD_TYPE_NORMAL_BUTTON;
16239       checked = FALSE;
16240       event_mask = GD_EVENT_RELEASED;
16241     }
16242     else if (id == GAME_CTRL_ID_UNDO ||
16243              id == GAME_CTRL_ID_REDO)
16244     {
16245       button_type = GD_TYPE_NORMAL_BUTTON;
16246       checked = FALSE;
16247       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16248     }
16249     else
16250     {
16251       button_type = GD_TYPE_CHECK_BUTTON;
16252       checked = (gamebutton_info[i].setup_value != NULL ?
16253                  *gamebutton_info[i].setup_value : FALSE);
16254       event_mask = GD_EVENT_PRESSED;
16255     }
16256
16257     gi = CreateGadget(GDI_CUSTOM_ID, id,
16258                       GDI_IMAGE_ID, graphic,
16259                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16260                       GDI_X, base_x + x,
16261                       GDI_Y, base_y + y,
16262                       GDI_WIDTH, gfx->width,
16263                       GDI_HEIGHT, gfx->height,
16264                       GDI_TYPE, button_type,
16265                       GDI_STATE, GD_BUTTON_UNPRESSED,
16266                       GDI_CHECKED, checked,
16267                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16268                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16269                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16270                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16271                       GDI_DIRECT_DRAW, FALSE,
16272                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16273                       GDI_EVENT_MASK, event_mask,
16274                       GDI_CALLBACK_ACTION, HandleGameButtons,
16275                       GDI_END);
16276
16277     if (gi == NULL)
16278       Fail("cannot create gadget");
16279
16280     game_gadget[id] = gi;
16281   }
16282 }
16283
16284 void FreeGameButtons(void)
16285 {
16286   int i;
16287
16288   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16289     FreeGadget(game_gadget[i]);
16290 }
16291
16292 static void UnmapGameButtonsAtSamePosition(int id)
16293 {
16294   int i;
16295
16296   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16297     if (i != id &&
16298         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16299         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16300       UnmapGadget(game_gadget[i]);
16301 }
16302
16303 static void UnmapGameButtonsAtSamePosition_All(void)
16304 {
16305   if (setup.show_load_save_buttons)
16306   {
16307     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16308     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16309     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16310   }
16311   else if (setup.show_undo_redo_buttons)
16312   {
16313     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16314     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16315     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16316   }
16317   else
16318   {
16319     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16320     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16321     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16322
16323     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16324     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16325     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16326   }
16327 }
16328
16329 void MapLoadSaveButtons(void)
16330 {
16331   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16332   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16333
16334   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16335   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16336 }
16337
16338 void MapUndoRedoButtons(void)
16339 {
16340   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16341   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16342
16343   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16344   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16345 }
16346
16347 void ModifyPauseButtons(void)
16348 {
16349   static int ids[] =
16350   {
16351     GAME_CTRL_ID_PAUSE,
16352     GAME_CTRL_ID_PAUSE2,
16353     GAME_CTRL_ID_PANEL_PAUSE,
16354     GAME_CTRL_ID_TOUCH_PAUSE,
16355     -1
16356   };
16357   int i;
16358
16359   for (i = 0; ids[i] > -1; i++)
16360     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16361 }
16362
16363 static void MapGameButtonsExt(boolean on_tape)
16364 {
16365   int i;
16366
16367   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16368   {
16369     if ((i == GAME_CTRL_ID_UNDO ||
16370          i == GAME_CTRL_ID_REDO) &&
16371         game_status != GAME_MODE_PLAYING)
16372       continue;
16373
16374     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16375       MapGadget(game_gadget[i]);
16376   }
16377
16378   UnmapGameButtonsAtSamePosition_All();
16379
16380   RedrawGameButtons();
16381 }
16382
16383 static void UnmapGameButtonsExt(boolean on_tape)
16384 {
16385   int i;
16386
16387   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16388     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16389       UnmapGadget(game_gadget[i]);
16390 }
16391
16392 static void RedrawGameButtonsExt(boolean on_tape)
16393 {
16394   int i;
16395
16396   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16397     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16398       RedrawGadget(game_gadget[i]);
16399 }
16400
16401 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16402 {
16403   if (gi == NULL)
16404     return;
16405
16406   gi->checked = state;
16407 }
16408
16409 static void RedrawSoundButtonGadget(int id)
16410 {
16411   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16412              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16413              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16414              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16415              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16416              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16417              id);
16418
16419   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16420   RedrawGadget(game_gadget[id2]);
16421 }
16422
16423 void MapGameButtons(void)
16424 {
16425   MapGameButtonsExt(FALSE);
16426 }
16427
16428 void UnmapGameButtons(void)
16429 {
16430   UnmapGameButtonsExt(FALSE);
16431 }
16432
16433 void RedrawGameButtons(void)
16434 {
16435   RedrawGameButtonsExt(FALSE);
16436 }
16437
16438 void MapGameButtonsOnTape(void)
16439 {
16440   MapGameButtonsExt(TRUE);
16441 }
16442
16443 void UnmapGameButtonsOnTape(void)
16444 {
16445   UnmapGameButtonsExt(TRUE);
16446 }
16447
16448 void RedrawGameButtonsOnTape(void)
16449 {
16450   RedrawGameButtonsExt(TRUE);
16451 }
16452
16453 static void GameUndoRedoExt(void)
16454 {
16455   ClearPlayerAction();
16456
16457   tape.pausing = TRUE;
16458
16459   RedrawPlayfield();
16460   UpdateAndDisplayGameControlValues();
16461
16462   DrawCompleteVideoDisplay();
16463   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16464   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16465   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16466
16467   ModifyPauseButtons();
16468
16469   BackToFront();
16470 }
16471
16472 static void GameUndo(int steps)
16473 {
16474   if (!CheckEngineSnapshotList())
16475     return;
16476
16477   int tape_property_bits = tape.property_bits;
16478
16479   LoadEngineSnapshot_Undo(steps);
16480
16481   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16482
16483   GameUndoRedoExt();
16484 }
16485
16486 static void GameRedo(int steps)
16487 {
16488   if (!CheckEngineSnapshotList())
16489     return;
16490
16491   int tape_property_bits = tape.property_bits;
16492
16493   LoadEngineSnapshot_Redo(steps);
16494
16495   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16496
16497   GameUndoRedoExt();
16498 }
16499
16500 static void HandleGameButtonsExt(int id, int button)
16501 {
16502   static boolean game_undo_executed = FALSE;
16503   int steps = BUTTON_STEPSIZE(button);
16504   boolean handle_game_buttons =
16505     (game_status == GAME_MODE_PLAYING ||
16506      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16507
16508   if (!handle_game_buttons)
16509     return;
16510
16511   switch (id)
16512   {
16513     case GAME_CTRL_ID_STOP:
16514     case GAME_CTRL_ID_PANEL_STOP:
16515     case GAME_CTRL_ID_TOUCH_STOP:
16516       TapeStopGame();
16517
16518       break;
16519
16520     case GAME_CTRL_ID_PAUSE:
16521     case GAME_CTRL_ID_PAUSE2:
16522     case GAME_CTRL_ID_PANEL_PAUSE:
16523     case GAME_CTRL_ID_TOUCH_PAUSE:
16524       if (network.enabled && game_status == GAME_MODE_PLAYING)
16525       {
16526         if (tape.pausing)
16527           SendToServer_ContinuePlaying();
16528         else
16529           SendToServer_PausePlaying();
16530       }
16531       else
16532         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16533
16534       game_undo_executed = FALSE;
16535
16536       break;
16537
16538     case GAME_CTRL_ID_PLAY:
16539     case GAME_CTRL_ID_PANEL_PLAY:
16540       if (game_status == GAME_MODE_MAIN)
16541       {
16542         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16543       }
16544       else if (tape.pausing)
16545       {
16546         if (network.enabled)
16547           SendToServer_ContinuePlaying();
16548         else
16549           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16550       }
16551       break;
16552
16553     case GAME_CTRL_ID_UNDO:
16554       // Important: When using "save snapshot when collecting an item" mode,
16555       // load last (current) snapshot for first "undo" after pressing "pause"
16556       // (else the last-but-one snapshot would be loaded, because the snapshot
16557       // pointer already points to the last snapshot when pressing "pause",
16558       // which is fine for "every step/move" mode, but not for "every collect")
16559       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16560           !game_undo_executed)
16561         steps--;
16562
16563       game_undo_executed = TRUE;
16564
16565       GameUndo(steps);
16566       break;
16567
16568     case GAME_CTRL_ID_REDO:
16569       GameRedo(steps);
16570       break;
16571
16572     case GAME_CTRL_ID_SAVE:
16573       TapeQuickSave();
16574       break;
16575
16576     case GAME_CTRL_ID_LOAD:
16577       TapeQuickLoad();
16578       break;
16579
16580     case SOUND_CTRL_ID_MUSIC:
16581     case SOUND_CTRL_ID_PANEL_MUSIC:
16582       if (setup.sound_music)
16583       { 
16584         setup.sound_music = FALSE;
16585
16586         FadeMusic();
16587       }
16588       else if (audio.music_available)
16589       { 
16590         setup.sound = setup.sound_music = TRUE;
16591
16592         SetAudioMode(setup.sound);
16593
16594         if (game_status == GAME_MODE_PLAYING)
16595           PlayLevelMusic();
16596       }
16597
16598       RedrawSoundButtonGadget(id);
16599
16600       break;
16601
16602     case SOUND_CTRL_ID_LOOPS:
16603     case SOUND_CTRL_ID_PANEL_LOOPS:
16604       if (setup.sound_loops)
16605         setup.sound_loops = FALSE;
16606       else if (audio.loops_available)
16607       {
16608         setup.sound = setup.sound_loops = TRUE;
16609
16610         SetAudioMode(setup.sound);
16611       }
16612
16613       RedrawSoundButtonGadget(id);
16614
16615       break;
16616
16617     case SOUND_CTRL_ID_SIMPLE:
16618     case SOUND_CTRL_ID_PANEL_SIMPLE:
16619       if (setup.sound_simple)
16620         setup.sound_simple = FALSE;
16621       else if (audio.sound_available)
16622       {
16623         setup.sound = setup.sound_simple = TRUE;
16624
16625         SetAudioMode(setup.sound);
16626       }
16627
16628       RedrawSoundButtonGadget(id);
16629
16630       break;
16631
16632     default:
16633       break;
16634   }
16635 }
16636
16637 static void HandleGameButtons(struct GadgetInfo *gi)
16638 {
16639   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16640 }
16641
16642 void HandleSoundButtonKeys(Key key)
16643 {
16644   if (key == setup.shortcut.sound_simple)
16645     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16646   else if (key == setup.shortcut.sound_loops)
16647     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16648   else if (key == setup.shortcut.sound_music)
16649     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16650 }