code cleanup
[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 WallGrowing(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 CheckWallGrowing(int ax, int ay)
9711 {
9712   int element = Tile[ax][ay];
9713   int graphic = el2img(element);
9714   boolean free_top    = FALSE;
9715   boolean free_bottom = FALSE;
9716   boolean free_left   = FALSE;
9717   boolean free_right  = FALSE;
9718   boolean stop_top    = FALSE;
9719   boolean stop_bottom = FALSE;
9720   boolean stop_left   = FALSE;
9721   boolean stop_right  = FALSE;
9722   boolean new_wall    = FALSE;
9723
9724   if (IS_ANIMATED(graphic))
9725     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9726
9727   if (!MovDelay[ax][ay])        // start building new wall
9728     MovDelay[ax][ay] = 6;
9729
9730   if (MovDelay[ax][ay])         // wait some time before building new wall
9731   {
9732     MovDelay[ax][ay]--;
9733     if (MovDelay[ax][ay])
9734       return;
9735   }
9736
9737   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9738     free_top = TRUE;
9739   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9740     free_bottom = TRUE;
9741   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9742     free_left = TRUE;
9743   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9744     free_right = TRUE;
9745
9746   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9747       element == EL_EXPANDABLE_WALL_ANY)
9748   {
9749     if (free_top)
9750     {
9751       Tile[ax][ay - 1] = EL_EXPANDABLE_WALL_GROWING;
9752       Store[ax][ay - 1] = element;
9753       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9754
9755       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9756         DrawLevelGraphic(ax, ay - 1, IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9757
9758       new_wall = TRUE;
9759     }
9760
9761     if (free_bottom)
9762     {
9763       Tile[ax][ay + 1] = EL_EXPANDABLE_WALL_GROWING;
9764       Store[ax][ay + 1] = element;
9765       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9766
9767       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9768         DrawLevelGraphic(ax, ay + 1, IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9769
9770       new_wall = TRUE;
9771     }
9772   }
9773
9774   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9775       element == EL_EXPANDABLE_WALL_ANY ||
9776       element == EL_EXPANDABLE_WALL ||
9777       element == EL_BD_EXPANDABLE_WALL)
9778   {
9779     if (free_left)
9780     {
9781       Tile[ax - 1][ay] = EL_EXPANDABLE_WALL_GROWING;
9782       Store[ax - 1][ay] = element;
9783       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9784
9785       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9786         DrawLevelGraphic(ax - 1, ay, IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9787
9788       new_wall = TRUE;
9789     }
9790
9791     if (free_right)
9792     {
9793       Tile[ax + 1][ay] = EL_EXPANDABLE_WALL_GROWING;
9794       Store[ax + 1][ay] = element;
9795       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9796
9797       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9798         DrawLevelGraphic(ax + 1, ay, IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9799
9800       new_wall = TRUE;
9801     }
9802   }
9803
9804   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9805     TEST_DrawLevelField(ax, ay);
9806
9807   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9808     stop_top = TRUE;
9809   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9810     stop_bottom = TRUE;
9811   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9812     stop_left = TRUE;
9813   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9814     stop_right = TRUE;
9815
9816   if (((stop_top && stop_bottom) ||
9817        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9818        element == EL_EXPANDABLE_WALL) &&
9819       ((stop_left && stop_right) ||
9820        element == EL_EXPANDABLE_WALL_VERTICAL))
9821     Tile[ax][ay] = EL_WALL;
9822
9823   if (new_wall)
9824     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9825 }
9826
9827 static void CheckSteelWallGrowing(int ax, int ay)
9828 {
9829   int element = Tile[ax][ay];
9830   int graphic = el2img(element);
9831   boolean free_top    = FALSE;
9832   boolean free_bottom = FALSE;
9833   boolean free_left   = FALSE;
9834   boolean free_right  = FALSE;
9835   boolean stop_top    = FALSE;
9836   boolean stop_bottom = FALSE;
9837   boolean stop_left   = FALSE;
9838   boolean stop_right  = FALSE;
9839   boolean new_wall    = FALSE;
9840
9841   if (IS_ANIMATED(graphic))
9842     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9843
9844   if (!MovDelay[ax][ay])        // start building new wall
9845     MovDelay[ax][ay] = 6;
9846
9847   if (MovDelay[ax][ay])         // wait some time before building new wall
9848   {
9849     MovDelay[ax][ay]--;
9850     if (MovDelay[ax][ay])
9851       return;
9852   }
9853
9854   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9855     free_top = TRUE;
9856   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9857     free_bottom = TRUE;
9858   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9859     free_left = TRUE;
9860   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9861     free_right = TRUE;
9862
9863   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9864       element == EL_EXPANDABLE_STEELWALL_ANY)
9865   {
9866     if (free_top)
9867     {
9868       Tile[ax][ay - 1] = EL_EXPANDABLE_STEELWALL_GROWING;
9869       Store[ax][ay - 1] = element;
9870       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9871
9872       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9873         DrawLevelGraphic(ax, ay - 1, IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9874
9875       new_wall = TRUE;
9876     }
9877
9878     if (free_bottom)
9879     {
9880       Tile[ax][ay + 1] = EL_EXPANDABLE_STEELWALL_GROWING;
9881       Store[ax][ay + 1] = element;
9882       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9883
9884       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9885         DrawLevelGraphic(ax, ay + 1, IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9886
9887       new_wall = TRUE;
9888     }
9889   }
9890
9891   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9892       element == EL_EXPANDABLE_STEELWALL_ANY)
9893   {
9894     if (free_left)
9895     {
9896       Tile[ax - 1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9897       Store[ax - 1][ay] = element;
9898       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9899
9900       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9901         DrawLevelGraphic(ax - 1, ay, IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9902
9903       new_wall = TRUE;
9904     }
9905
9906     if (free_right)
9907     {
9908       Tile[ax + 1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9909       Store[ax + 1][ay] = element;
9910       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9911
9912       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9913         DrawLevelGraphic(ax + 1, ay, IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9914
9915       new_wall = TRUE;
9916     }
9917   }
9918
9919   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9920     stop_top = TRUE;
9921   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9922     stop_bottom = TRUE;
9923   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9924     stop_left = TRUE;
9925   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9926     stop_right = TRUE;
9927
9928   if (((stop_top && stop_bottom) ||
9929        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9930       ((stop_left && stop_right) ||
9931        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9932     Tile[ax][ay] = EL_STEELWALL;
9933
9934   if (new_wall)
9935     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9936 }
9937
9938 static void CheckForDragon(int x, int y)
9939 {
9940   int i, j;
9941   boolean dragon_found = FALSE;
9942   struct XY *xy = xy_topdown;
9943
9944   for (i = 0; i < NUM_DIRECTIONS; i++)
9945   {
9946     for (j = 0; j < 4; j++)
9947     {
9948       int xx = x + j * xy[i].x;
9949       int yy = y + j * xy[i].y;
9950
9951       if (IN_LEV_FIELD(xx, yy) &&
9952           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9953       {
9954         if (Tile[xx][yy] == EL_DRAGON)
9955           dragon_found = TRUE;
9956       }
9957       else
9958         break;
9959     }
9960   }
9961
9962   if (!dragon_found)
9963   {
9964     for (i = 0; i < NUM_DIRECTIONS; i++)
9965     {
9966       for (j = 0; j < 3; j++)
9967       {
9968         int xx = x + j * xy[i].x;
9969         int yy = y + j * xy[i].y;
9970
9971         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9972         {
9973           Tile[xx][yy] = EL_EMPTY;
9974           TEST_DrawLevelField(xx, yy);
9975         }
9976         else
9977           break;
9978       }
9979     }
9980   }
9981 }
9982
9983 static void InitBuggyBase(int x, int y)
9984 {
9985   int element = Tile[x][y];
9986   int activating_delay = FRAMES_PER_SECOND / 4;
9987
9988   ChangeDelay[x][y] =
9989     (element == EL_SP_BUGGY_BASE ?
9990      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9991      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9992      activating_delay :
9993      element == EL_SP_BUGGY_BASE_ACTIVE ?
9994      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9995 }
9996
9997 static void WarnBuggyBase(int x, int y)
9998 {
9999   int i;
10000   struct XY *xy = xy_topdown;
10001
10002   for (i = 0; i < NUM_DIRECTIONS; i++)
10003   {
10004     int xx = x + xy[i].x;
10005     int yy = y + xy[i].y;
10006
10007     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10008     {
10009       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10010
10011       break;
10012     }
10013   }
10014 }
10015
10016 static void InitTrap(int x, int y)
10017 {
10018   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10019 }
10020
10021 static void ActivateTrap(int x, int y)
10022 {
10023   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10024 }
10025
10026 static void ChangeActiveTrap(int x, int y)
10027 {
10028   int graphic = IMG_TRAP_ACTIVE;
10029
10030   // if new animation frame was drawn, correct crumbled sand border
10031   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10032     TEST_DrawLevelFieldCrumbled(x, y);
10033 }
10034
10035 static int getSpecialActionElement(int element, int number, int base_element)
10036 {
10037   return (element != EL_EMPTY ? element :
10038           number != -1 ? base_element + number - 1 :
10039           EL_EMPTY);
10040 }
10041
10042 static int getModifiedActionNumber(int value_old, int operator, int operand,
10043                                    int value_min, int value_max)
10044 {
10045   int value_new = (operator == CA_MODE_SET      ? operand :
10046                    operator == CA_MODE_ADD      ? value_old + operand :
10047                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10048                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10049                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10050                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10051                    value_old);
10052
10053   return (value_new < value_min ? value_min :
10054           value_new > value_max ? value_max :
10055           value_new);
10056 }
10057
10058 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10059 {
10060   struct ElementInfo *ei = &element_info[element];
10061   struct ElementChangeInfo *change = &ei->change_page[page];
10062   int target_element = change->target_element;
10063   int action_type = change->action_type;
10064   int action_mode = change->action_mode;
10065   int action_arg = change->action_arg;
10066   int action_element = change->action_element;
10067   int i;
10068
10069   if (!change->has_action)
10070     return;
10071
10072   // ---------- determine action paramater values -----------------------------
10073
10074   int level_time_value =
10075     (level.time > 0 ? TimeLeft :
10076      TimePlayed);
10077
10078   int action_arg_element_raw =
10079     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10080      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10081      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10082      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10083      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10084      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10085      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10086      EL_EMPTY);
10087   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10088
10089   int action_arg_direction =
10090     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10091      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10092      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10093      change->actual_trigger_side :
10094      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10095      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10096      MV_NONE);
10097
10098   int action_arg_number_min =
10099     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10100      CA_ARG_MIN);
10101
10102   int action_arg_number_max =
10103     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10104      action_type == CA_SET_LEVEL_GEMS ? 999 :
10105      action_type == CA_SET_LEVEL_TIME ? 9999 :
10106      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10107      action_type == CA_SET_CE_VALUE ? 9999 :
10108      action_type == CA_SET_CE_SCORE ? 9999 :
10109      CA_ARG_MAX);
10110
10111   int action_arg_number_reset =
10112     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10113      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10114      action_type == CA_SET_LEVEL_TIME ? level.time :
10115      action_type == CA_SET_LEVEL_SCORE ? 0 :
10116      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10117      action_type == CA_SET_CE_SCORE ? 0 :
10118      0);
10119
10120   int action_arg_number =
10121     (action_arg <= CA_ARG_MAX ? action_arg :
10122      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10123      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10124      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10125      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10126      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10127      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10128      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10129      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10130      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10131      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10132      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10133      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10134      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10135      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10136      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10137      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10138      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10139      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10140      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10141      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10142      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10143      -1);
10144
10145   int action_arg_number_old =
10146     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10147      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10148      action_type == CA_SET_LEVEL_SCORE ? game.score :
10149      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10150      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10151      0);
10152
10153   int action_arg_number_new =
10154     getModifiedActionNumber(action_arg_number_old,
10155                             action_mode, action_arg_number,
10156                             action_arg_number_min, action_arg_number_max);
10157
10158   int trigger_player_bits =
10159     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10160      change->actual_trigger_player_bits : change->trigger_player);
10161
10162   int action_arg_player_bits =
10163     (action_arg >= CA_ARG_PLAYER_1 &&
10164      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10165      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10166      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10167      PLAYER_BITS_ANY);
10168
10169   // ---------- execute action  -----------------------------------------------
10170
10171   switch (action_type)
10172   {
10173     case CA_NO_ACTION:
10174     {
10175       return;
10176     }
10177
10178     // ---------- level actions  ----------------------------------------------
10179
10180     case CA_RESTART_LEVEL:
10181     {
10182       game.restart_level = TRUE;
10183
10184       break;
10185     }
10186
10187     case CA_SHOW_ENVELOPE:
10188     {
10189       int element = getSpecialActionElement(action_arg_element,
10190                                             action_arg_number, EL_ENVELOPE_1);
10191
10192       if (IS_ENVELOPE(element))
10193         local_player->show_envelope = element;
10194
10195       break;
10196     }
10197
10198     case CA_SET_LEVEL_TIME:
10199     {
10200       if (level.time > 0)       // only modify limited time value
10201       {
10202         TimeLeft = action_arg_number_new;
10203
10204         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10205
10206         DisplayGameControlValues();
10207
10208         if (!TimeLeft && game.time_limit)
10209           for (i = 0; i < MAX_PLAYERS; i++)
10210             KillPlayer(&stored_player[i]);
10211       }
10212
10213       break;
10214     }
10215
10216     case CA_SET_LEVEL_SCORE:
10217     {
10218       game.score = action_arg_number_new;
10219
10220       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10221
10222       DisplayGameControlValues();
10223
10224       break;
10225     }
10226
10227     case CA_SET_LEVEL_GEMS:
10228     {
10229       game.gems_still_needed = action_arg_number_new;
10230
10231       game.snapshot.collected_item = TRUE;
10232
10233       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10234
10235       DisplayGameControlValues();
10236
10237       break;
10238     }
10239
10240     case CA_SET_LEVEL_WIND:
10241     {
10242       game.wind_direction = action_arg_direction;
10243
10244       break;
10245     }
10246
10247     case CA_SET_LEVEL_RANDOM_SEED:
10248     {
10249       // ensure that setting a new random seed while playing is predictable
10250       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10251
10252       break;
10253     }
10254
10255     // ---------- player actions  ---------------------------------------------
10256
10257     case CA_MOVE_PLAYER:
10258     case CA_MOVE_PLAYER_NEW:
10259     {
10260       // automatically move to the next field in specified direction
10261       for (i = 0; i < MAX_PLAYERS; i++)
10262         if (trigger_player_bits & (1 << i))
10263           if (action_type == CA_MOVE_PLAYER ||
10264               stored_player[i].MovPos == 0)
10265             stored_player[i].programmed_action = action_arg_direction;
10266
10267       break;
10268     }
10269
10270     case CA_EXIT_PLAYER:
10271     {
10272       for (i = 0; i < MAX_PLAYERS; i++)
10273         if (action_arg_player_bits & (1 << i))
10274           ExitPlayer(&stored_player[i]);
10275
10276       if (game.players_still_needed == 0)
10277         LevelSolved();
10278
10279       break;
10280     }
10281
10282     case CA_KILL_PLAYER:
10283     {
10284       for (i = 0; i < MAX_PLAYERS; i++)
10285         if (action_arg_player_bits & (1 << i))
10286           KillPlayer(&stored_player[i]);
10287
10288       break;
10289     }
10290
10291     case CA_SET_PLAYER_KEYS:
10292     {
10293       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10294       int element = getSpecialActionElement(action_arg_element,
10295                                             action_arg_number, EL_KEY_1);
10296
10297       if (IS_KEY(element))
10298       {
10299         for (i = 0; i < MAX_PLAYERS; i++)
10300         {
10301           if (trigger_player_bits & (1 << i))
10302           {
10303             stored_player[i].key[KEY_NR(element)] = key_state;
10304
10305             DrawGameDoorValues();
10306           }
10307         }
10308       }
10309
10310       break;
10311     }
10312
10313     case CA_SET_PLAYER_SPEED:
10314     {
10315       for (i = 0; i < MAX_PLAYERS; i++)
10316       {
10317         if (trigger_player_bits & (1 << i))
10318         {
10319           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10320
10321           if (action_arg == CA_ARG_SPEED_FASTER &&
10322               stored_player[i].cannot_move)
10323           {
10324             action_arg_number = STEPSIZE_VERY_SLOW;
10325           }
10326           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10327                    action_arg == CA_ARG_SPEED_FASTER)
10328           {
10329             action_arg_number = 2;
10330             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10331                            CA_MODE_MULTIPLY);
10332           }
10333           else if (action_arg == CA_ARG_NUMBER_RESET)
10334           {
10335             action_arg_number = level.initial_player_stepsize[i];
10336           }
10337
10338           move_stepsize =
10339             getModifiedActionNumber(move_stepsize,
10340                                     action_mode,
10341                                     action_arg_number,
10342                                     action_arg_number_min,
10343                                     action_arg_number_max);
10344
10345           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10346         }
10347       }
10348
10349       break;
10350     }
10351
10352     case CA_SET_PLAYER_SHIELD:
10353     {
10354       for (i = 0; i < MAX_PLAYERS; i++)
10355       {
10356         if (trigger_player_bits & (1 << i))
10357         {
10358           if (action_arg == CA_ARG_SHIELD_OFF)
10359           {
10360             stored_player[i].shield_normal_time_left = 0;
10361             stored_player[i].shield_deadly_time_left = 0;
10362           }
10363           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10364           {
10365             stored_player[i].shield_normal_time_left = 999999;
10366           }
10367           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10368           {
10369             stored_player[i].shield_normal_time_left = 999999;
10370             stored_player[i].shield_deadly_time_left = 999999;
10371           }
10372         }
10373       }
10374
10375       break;
10376     }
10377
10378     case CA_SET_PLAYER_GRAVITY:
10379     {
10380       for (i = 0; i < MAX_PLAYERS; i++)
10381       {
10382         if (trigger_player_bits & (1 << i))
10383         {
10384           stored_player[i].gravity =
10385             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10386              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10387              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10388              stored_player[i].gravity);
10389         }
10390       }
10391
10392       break;
10393     }
10394
10395     case CA_SET_PLAYER_ARTWORK:
10396     {
10397       for (i = 0; i < MAX_PLAYERS; i++)
10398       {
10399         if (trigger_player_bits & (1 << i))
10400         {
10401           int artwork_element = action_arg_element;
10402
10403           if (action_arg == CA_ARG_ELEMENT_RESET)
10404             artwork_element =
10405               (level.use_artwork_element[i] ? level.artwork_element[i] :
10406                stored_player[i].element_nr);
10407
10408           if (stored_player[i].artwork_element != artwork_element)
10409             stored_player[i].Frame = 0;
10410
10411           stored_player[i].artwork_element = artwork_element;
10412
10413           SetPlayerWaiting(&stored_player[i], FALSE);
10414
10415           // set number of special actions for bored and sleeping animation
10416           stored_player[i].num_special_action_bored =
10417             get_num_special_action(artwork_element,
10418                                    ACTION_BORING_1, ACTION_BORING_LAST);
10419           stored_player[i].num_special_action_sleeping =
10420             get_num_special_action(artwork_element,
10421                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10422         }
10423       }
10424
10425       break;
10426     }
10427
10428     case CA_SET_PLAYER_INVENTORY:
10429     {
10430       for (i = 0; i < MAX_PLAYERS; i++)
10431       {
10432         struct PlayerInfo *player = &stored_player[i];
10433         int j, k;
10434
10435         if (trigger_player_bits & (1 << i))
10436         {
10437           int inventory_element = action_arg_element;
10438
10439           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10440               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10441               action_arg == CA_ARG_ELEMENT_ACTION)
10442           {
10443             int element = inventory_element;
10444             int collect_count = element_info[element].collect_count_initial;
10445
10446             if (!IS_CUSTOM_ELEMENT(element))
10447               collect_count = 1;
10448
10449             if (collect_count == 0)
10450               player->inventory_infinite_element = element;
10451             else
10452               for (k = 0; k < collect_count; k++)
10453                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10454                   player->inventory_element[player->inventory_size++] =
10455                     element;
10456           }
10457           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10458                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10459                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10460           {
10461             if (player->inventory_infinite_element != EL_UNDEFINED &&
10462                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10463                                      action_arg_element_raw))
10464               player->inventory_infinite_element = EL_UNDEFINED;
10465
10466             for (k = 0, j = 0; j < player->inventory_size; j++)
10467             {
10468               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10469                                         action_arg_element_raw))
10470                 player->inventory_element[k++] = player->inventory_element[j];
10471             }
10472
10473             player->inventory_size = k;
10474           }
10475           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10476           {
10477             if (player->inventory_size > 0)
10478             {
10479               for (j = 0; j < player->inventory_size - 1; j++)
10480                 player->inventory_element[j] = player->inventory_element[j + 1];
10481
10482               player->inventory_size--;
10483             }
10484           }
10485           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10486           {
10487             if (player->inventory_size > 0)
10488               player->inventory_size--;
10489           }
10490           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10491           {
10492             player->inventory_infinite_element = EL_UNDEFINED;
10493             player->inventory_size = 0;
10494           }
10495           else if (action_arg == CA_ARG_INVENTORY_RESET)
10496           {
10497             player->inventory_infinite_element = EL_UNDEFINED;
10498             player->inventory_size = 0;
10499
10500             if (level.use_initial_inventory[i])
10501             {
10502               for (j = 0; j < level.initial_inventory_size[i]; j++)
10503               {
10504                 int element = level.initial_inventory_content[i][j];
10505                 int collect_count = element_info[element].collect_count_initial;
10506
10507                 if (!IS_CUSTOM_ELEMENT(element))
10508                   collect_count = 1;
10509
10510                 if (collect_count == 0)
10511                   player->inventory_infinite_element = element;
10512                 else
10513                   for (k = 0; k < collect_count; k++)
10514                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10515                       player->inventory_element[player->inventory_size++] =
10516                         element;
10517               }
10518             }
10519           }
10520         }
10521       }
10522
10523       break;
10524     }
10525
10526     // ---------- CE actions  -------------------------------------------------
10527
10528     case CA_SET_CE_VALUE:
10529     {
10530       int last_ce_value = CustomValue[x][y];
10531
10532       CustomValue[x][y] = action_arg_number_new;
10533
10534       if (CustomValue[x][y] != last_ce_value)
10535       {
10536         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10537         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10538
10539         if (CustomValue[x][y] == 0)
10540         {
10541           // reset change counter (else CE_VALUE_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_VALUE_GETS_ZERO);
10545           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10546         }
10547       }
10548
10549       break;
10550     }
10551
10552     case CA_SET_CE_SCORE:
10553     {
10554       int last_ce_score = ei->collect_score;
10555
10556       ei->collect_score = action_arg_number_new;
10557
10558       if (ei->collect_score != last_ce_score)
10559       {
10560         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10561         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10562
10563         if (ei->collect_score == 0)
10564         {
10565           int xx, yy;
10566
10567           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10568           ChangeCount[x][y] = 0;        // allow at least one more change
10569
10570           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10571           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10572
10573           /*
10574             This is a very special case that seems to be a mixture between
10575             CheckElementChange() and CheckTriggeredElementChange(): while
10576             the first one only affects single elements that are triggered
10577             directly, the second one affects multiple elements in the playfield
10578             that are triggered indirectly by another element. This is a third
10579             case: Changing the CE score always affects multiple identical CEs,
10580             so every affected CE must be checked, not only the single CE for
10581             which the CE score was changed in the first place (as every instance
10582             of that CE shares the same CE score, and therefore also can change)!
10583           */
10584           SCAN_PLAYFIELD(xx, yy)
10585           {
10586             if (Tile[xx][yy] == element)
10587               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10588                                  CE_SCORE_GETS_ZERO);
10589           }
10590         }
10591       }
10592
10593       break;
10594     }
10595
10596     case CA_SET_CE_ARTWORK:
10597     {
10598       int artwork_element = action_arg_element;
10599       boolean reset_frame = FALSE;
10600       int xx, yy;
10601
10602       if (action_arg == CA_ARG_ELEMENT_RESET)
10603         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10604                            element);
10605
10606       if (ei->gfx_element != artwork_element)
10607         reset_frame = TRUE;
10608
10609       ei->gfx_element = artwork_element;
10610
10611       SCAN_PLAYFIELD(xx, yy)
10612       {
10613         if (Tile[xx][yy] == element)
10614         {
10615           if (reset_frame)
10616           {
10617             ResetGfxAnimation(xx, yy);
10618             ResetRandomAnimationValue(xx, yy);
10619           }
10620
10621           TEST_DrawLevelField(xx, yy);
10622         }
10623       }
10624
10625       break;
10626     }
10627
10628     // ---------- engine actions  ---------------------------------------------
10629
10630     case CA_SET_ENGINE_SCAN_MODE:
10631     {
10632       InitPlayfieldScanMode(action_arg);
10633
10634       break;
10635     }
10636
10637     default:
10638       break;
10639   }
10640 }
10641
10642 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10643 {
10644   int old_element = Tile[x][y];
10645   int new_element = GetElementFromGroupElement(element);
10646   int previous_move_direction = MovDir[x][y];
10647   int last_ce_value = CustomValue[x][y];
10648   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10649   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10650   boolean add_player_onto_element = (new_element_is_player &&
10651                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10652                                      IS_WALKABLE(old_element));
10653
10654   if (!add_player_onto_element)
10655   {
10656     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10657       RemoveMovingField(x, y);
10658     else
10659       RemoveField(x, y);
10660
10661     Tile[x][y] = new_element;
10662
10663     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10664       MovDir[x][y] = previous_move_direction;
10665
10666     if (element_info[new_element].use_last_ce_value)
10667       CustomValue[x][y] = last_ce_value;
10668
10669     InitField_WithBug1(x, y, FALSE);
10670
10671     new_element = Tile[x][y];   // element may have changed
10672
10673     ResetGfxAnimation(x, y);
10674     ResetRandomAnimationValue(x, y);
10675
10676     TEST_DrawLevelField(x, y);
10677
10678     if (GFX_CRUMBLED(new_element))
10679       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10680   }
10681
10682   // check if element under the player changes from accessible to unaccessible
10683   // (needed for special case of dropping element which then changes)
10684   // (must be checked after creating new element for walkable group elements)
10685   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10686       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10687   {
10688     Bang(x, y);
10689
10690     return;
10691   }
10692
10693   // "ChangeCount" not set yet to allow "entered by player" change one time
10694   if (new_element_is_player)
10695     RelocatePlayer(x, y, new_element);
10696
10697   if (is_change)
10698     ChangeCount[x][y]++;        // count number of changes in the same frame
10699
10700   TestIfBadThingTouchesPlayer(x, y);
10701   TestIfPlayerTouchesCustomElement(x, y);
10702   TestIfElementTouchesCustomElement(x, y);
10703 }
10704
10705 static void CreateField(int x, int y, int element)
10706 {
10707   CreateFieldExt(x, y, element, FALSE);
10708 }
10709
10710 static void CreateElementFromChange(int x, int y, int element)
10711 {
10712   element = GET_VALID_RUNTIME_ELEMENT(element);
10713
10714   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10715   {
10716     int old_element = Tile[x][y];
10717
10718     // prevent changed element from moving in same engine frame
10719     // unless both old and new element can either fall or move
10720     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10721         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10722       Stop[x][y] = TRUE;
10723   }
10724
10725   CreateFieldExt(x, y, element, TRUE);
10726 }
10727
10728 static boolean ChangeElement(int x, int y, int element, int page)
10729 {
10730   struct ElementInfo *ei = &element_info[element];
10731   struct ElementChangeInfo *change = &ei->change_page[page];
10732   int ce_value = CustomValue[x][y];
10733   int ce_score = ei->collect_score;
10734   int target_element;
10735   int old_element = Tile[x][y];
10736
10737   // always use default change event to prevent running into a loop
10738   if (ChangeEvent[x][y] == -1)
10739     ChangeEvent[x][y] = CE_DELAY;
10740
10741   if (ChangeEvent[x][y] == CE_DELAY)
10742   {
10743     // reset actual trigger element, trigger player and action element
10744     change->actual_trigger_element = EL_EMPTY;
10745     change->actual_trigger_player = EL_EMPTY;
10746     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10747     change->actual_trigger_side = CH_SIDE_NONE;
10748     change->actual_trigger_ce_value = 0;
10749     change->actual_trigger_ce_score = 0;
10750   }
10751
10752   // do not change elements more than a specified maximum number of changes
10753   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10754     return FALSE;
10755
10756   ChangeCount[x][y]++;          // count number of changes in the same frame
10757
10758   if (change->explode)
10759   {
10760     Bang(x, y);
10761
10762     return TRUE;
10763   }
10764
10765   if (change->use_target_content)
10766   {
10767     boolean complete_replace = TRUE;
10768     boolean can_replace[3][3];
10769     int xx, yy;
10770
10771     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10772     {
10773       boolean is_empty;
10774       boolean is_walkable;
10775       boolean is_diggable;
10776       boolean is_collectible;
10777       boolean is_removable;
10778       boolean is_destructible;
10779       int ex = x + xx - 1;
10780       int ey = y + yy - 1;
10781       int content_element = change->target_content.e[xx][yy];
10782       int e;
10783
10784       can_replace[xx][yy] = TRUE;
10785
10786       if (ex == x && ey == y)   // do not check changing element itself
10787         continue;
10788
10789       if (content_element == EL_EMPTY_SPACE)
10790       {
10791         can_replace[xx][yy] = FALSE;    // do not replace border with space
10792
10793         continue;
10794       }
10795
10796       if (!IN_LEV_FIELD(ex, ey))
10797       {
10798         can_replace[xx][yy] = FALSE;
10799         complete_replace = FALSE;
10800
10801         continue;
10802       }
10803
10804       e = Tile[ex][ey];
10805
10806       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10807         e = MovingOrBlocked2Element(ex, ey);
10808
10809       is_empty = (IS_FREE(ex, ey) ||
10810                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10811
10812       is_walkable     = (is_empty || IS_WALKABLE(e));
10813       is_diggable     = (is_empty || IS_DIGGABLE(e));
10814       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10815       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10816       is_removable    = (is_diggable || is_collectible);
10817
10818       can_replace[xx][yy] =
10819         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10820           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10821           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10822           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10823           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10824           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10825          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10826
10827       if (!can_replace[xx][yy])
10828         complete_replace = FALSE;
10829     }
10830
10831     if (!change->only_if_complete || complete_replace)
10832     {
10833       boolean something_has_changed = FALSE;
10834
10835       if (change->only_if_complete && change->use_random_replace &&
10836           RND(100) < change->random_percentage)
10837         return FALSE;
10838
10839       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10840       {
10841         int ex = x + xx - 1;
10842         int ey = y + yy - 1;
10843         int content_element;
10844
10845         if (can_replace[xx][yy] && (!change->use_random_replace ||
10846                                     RND(100) < change->random_percentage))
10847         {
10848           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10849             RemoveMovingField(ex, ey);
10850
10851           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10852
10853           content_element = change->target_content.e[xx][yy];
10854           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10855                                               ce_value, ce_score);
10856
10857           CreateElementFromChange(ex, ey, target_element);
10858
10859           something_has_changed = TRUE;
10860
10861           // for symmetry reasons, freeze newly created border elements
10862           if (ex != x || ey != y)
10863             Stop[ex][ey] = TRUE;        // no more moving in this frame
10864         }
10865       }
10866
10867       if (something_has_changed)
10868       {
10869         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10870         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10871       }
10872     }
10873   }
10874   else
10875   {
10876     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10877                                         ce_value, ce_score);
10878
10879     if (element == EL_DIAGONAL_GROWING ||
10880         element == EL_DIAGONAL_SHRINKING)
10881     {
10882       target_element = Store[x][y];
10883
10884       Store[x][y] = EL_EMPTY;
10885     }
10886
10887     // special case: element changes to player (and may be kept if walkable)
10888     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10889       CreateElementFromChange(x, y, EL_EMPTY);
10890
10891     CreateElementFromChange(x, y, target_element);
10892
10893     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10894     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10895   }
10896
10897   // this uses direct change before indirect change
10898   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10899
10900   return TRUE;
10901 }
10902
10903 static void HandleElementChange(int x, int y, int page)
10904 {
10905   int element = MovingOrBlocked2Element(x, y);
10906   struct ElementInfo *ei = &element_info[element];
10907   struct ElementChangeInfo *change = &ei->change_page[page];
10908   boolean handle_action_before_change = FALSE;
10909
10910 #ifdef DEBUG
10911   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10912       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10913   {
10914     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10915           x, y, element, element_info[element].token_name);
10916     Debug("game:playing:HandleElementChange", "This should never happen!");
10917   }
10918 #endif
10919
10920   // this can happen with classic bombs on walkable, changing elements
10921   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10922   {
10923     return;
10924   }
10925
10926   if (ChangeDelay[x][y] == 0)           // initialize element change
10927   {
10928     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10929
10930     if (change->can_change)
10931     {
10932       // !!! not clear why graphic animation should be reset at all here !!!
10933       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10934       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10935
10936       /*
10937         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10938
10939         When using an animation frame delay of 1 (this only happens with
10940         "sp_zonk.moving.left/right" in the classic graphics), the default
10941         (non-moving) animation shows wrong animation frames (while the
10942         moving animation, like "sp_zonk.moving.left/right", is correct,
10943         so this graphical bug never shows up with the classic graphics).
10944         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10945         be drawn instead of the correct frames 0,1,2,3. This is caused by
10946         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10947         an element change: First when the change delay ("ChangeDelay[][]")
10948         counter has reached zero after decrementing, then a second time in
10949         the next frame (after "GfxFrame[][]" was already incremented) when
10950         "ChangeDelay[][]" is reset to the initial delay value again.
10951
10952         This causes frame 0 to be drawn twice, while the last frame won't
10953         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10954
10955         As some animations may already be cleverly designed around this bug
10956         (at least the "Snake Bite" snake tail animation does this), it cannot
10957         simply be fixed here without breaking such existing animations.
10958         Unfortunately, it cannot easily be detected if a graphics set was
10959         designed "before" or "after" the bug was fixed. As a workaround,
10960         a new graphics set option "game.graphics_engine_version" was added
10961         to be able to specify the game's major release version for which the
10962         graphics set was designed, which can then be used to decide if the
10963         bugfix should be used (version 4 and above) or not (version 3 or
10964         below, or if no version was specified at all, as with old sets).
10965
10966         (The wrong/fixed animation frames can be tested with the test level set
10967         "test_gfxframe" and level "000", which contains a specially prepared
10968         custom element at level position (x/y) == (11/9) which uses the zonk
10969         animation mentioned above. Using "game.graphics_engine_version: 4"
10970         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10971         This can also be seen from the debug output for this test element.)
10972       */
10973
10974       // when a custom element is about to change (for example by change delay),
10975       // do not reset graphic animation when the custom element is moving
10976       if (game.graphics_engine_version < 4 &&
10977           !IS_MOVING(x, y))
10978       {
10979         ResetGfxAnimation(x, y);
10980         ResetRandomAnimationValue(x, y);
10981       }
10982
10983       if (change->pre_change_function)
10984         change->pre_change_function(x, y);
10985     }
10986   }
10987
10988   ChangeDelay[x][y]--;
10989
10990   if (ChangeDelay[x][y] != 0)           // continue element change
10991   {
10992     if (change->can_change)
10993     {
10994       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10995
10996       if (IS_ANIMATED(graphic))
10997         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10998
10999       if (change->change_function)
11000         change->change_function(x, y);
11001     }
11002   }
11003   else                                  // finish element change
11004   {
11005     if (ChangePage[x][y] != -1)         // remember page from delayed change
11006     {
11007       page = ChangePage[x][y];
11008       ChangePage[x][y] = -1;
11009
11010       change = &ei->change_page[page];
11011     }
11012
11013     if (IS_MOVING(x, y))                // never change a running system ;-)
11014     {
11015       ChangeDelay[x][y] = 1;            // try change after next move step
11016       ChangePage[x][y] = page;          // remember page to use for change
11017
11018       return;
11019     }
11020
11021     // special case: set new level random seed before changing element
11022     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11023       handle_action_before_change = TRUE;
11024
11025     if (change->has_action && handle_action_before_change)
11026       ExecuteCustomElementAction(x, y, element, page);
11027
11028     if (change->can_change)
11029     {
11030       if (ChangeElement(x, y, element, page))
11031       {
11032         if (change->post_change_function)
11033           change->post_change_function(x, y);
11034       }
11035     }
11036
11037     if (change->has_action && !handle_action_before_change)
11038       ExecuteCustomElementAction(x, y, element, page);
11039   }
11040 }
11041
11042 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11043                                               int trigger_element,
11044                                               int trigger_event,
11045                                               int trigger_player,
11046                                               int trigger_side,
11047                                               int trigger_page)
11048 {
11049   boolean change_done_any = FALSE;
11050   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11051   int i;
11052
11053   if (!(trigger_events[trigger_element][trigger_event]))
11054     return FALSE;
11055
11056   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11057
11058   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11059   {
11060     int element = EL_CUSTOM_START + i;
11061     boolean change_done = FALSE;
11062     int p;
11063
11064     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11065         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11066       continue;
11067
11068     for (p = 0; p < element_info[element].num_change_pages; p++)
11069     {
11070       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11071
11072       if (change->can_change_or_has_action &&
11073           change->has_event[trigger_event] &&
11074           change->trigger_side & trigger_side &&
11075           change->trigger_player & trigger_player &&
11076           change->trigger_page & trigger_page_bits &&
11077           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11078       {
11079         change->actual_trigger_element = trigger_element;
11080         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11081         change->actual_trigger_player_bits = trigger_player;
11082         change->actual_trigger_side = trigger_side;
11083         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11084         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11085
11086         if ((change->can_change && !change_done) || change->has_action)
11087         {
11088           int x, y;
11089
11090           SCAN_PLAYFIELD(x, y)
11091           {
11092             if (Tile[x][y] == element)
11093             {
11094               if (change->can_change && !change_done)
11095               {
11096                 // if element already changed in this frame, not only prevent
11097                 // another element change (checked in ChangeElement()), but
11098                 // also prevent additional element actions for this element
11099
11100                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11101                     !level.use_action_after_change_bug)
11102                   continue;
11103
11104                 ChangeDelay[x][y] = 1;
11105                 ChangeEvent[x][y] = trigger_event;
11106
11107                 HandleElementChange(x, y, p);
11108               }
11109               else if (change->has_action)
11110               {
11111                 // if element already changed in this frame, not only prevent
11112                 // another element change (checked in ChangeElement()), but
11113                 // also prevent additional element actions for this element
11114
11115                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11116                     !level.use_action_after_change_bug)
11117                   continue;
11118
11119                 ExecuteCustomElementAction(x, y, element, p);
11120                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11121               }
11122             }
11123           }
11124
11125           if (change->can_change)
11126           {
11127             change_done = TRUE;
11128             change_done_any = TRUE;
11129           }
11130         }
11131       }
11132     }
11133   }
11134
11135   RECURSION_LOOP_DETECTION_END();
11136
11137   return change_done_any;
11138 }
11139
11140 static boolean CheckElementChangeExt(int x, int y,
11141                                      int element,
11142                                      int trigger_element,
11143                                      int trigger_event,
11144                                      int trigger_player,
11145                                      int trigger_side)
11146 {
11147   boolean change_done = FALSE;
11148   int p;
11149
11150   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11151       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11152     return FALSE;
11153
11154   if (Tile[x][y] == EL_BLOCKED)
11155   {
11156     Blocked2Moving(x, y, &x, &y);
11157     element = Tile[x][y];
11158   }
11159
11160   // check if element has already changed or is about to change after moving
11161   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11162        Tile[x][y] != element) ||
11163
11164       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11165        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11166         ChangePage[x][y] != -1)))
11167     return FALSE;
11168
11169   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11170
11171   for (p = 0; p < element_info[element].num_change_pages; p++)
11172   {
11173     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11174
11175     /* check trigger element for all events where the element that is checked
11176        for changing interacts with a directly adjacent element -- this is
11177        different to element changes that affect other elements to change on the
11178        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11179     boolean check_trigger_element =
11180       (trigger_event == CE_NEXT_TO_X ||
11181        trigger_event == CE_TOUCHING_X ||
11182        trigger_event == CE_HITTING_X ||
11183        trigger_event == CE_HIT_BY_X ||
11184        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11185
11186     if (change->can_change_or_has_action &&
11187         change->has_event[trigger_event] &&
11188         change->trigger_side & trigger_side &&
11189         change->trigger_player & trigger_player &&
11190         (!check_trigger_element ||
11191          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11192     {
11193       change->actual_trigger_element = trigger_element;
11194       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11195       change->actual_trigger_player_bits = trigger_player;
11196       change->actual_trigger_side = trigger_side;
11197       change->actual_trigger_ce_value = CustomValue[x][y];
11198       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11199
11200       // special case: trigger element not at (x,y) position for some events
11201       if (check_trigger_element)
11202       {
11203         static struct
11204         {
11205           int dx, dy;
11206         } move_xy[] =
11207           {
11208             {  0,  0 },
11209             { -1,  0 },
11210             { +1,  0 },
11211             {  0,  0 },
11212             {  0, -1 },
11213             {  0,  0 }, { 0, 0 }, { 0, 0 },
11214             {  0, +1 }
11215           };
11216
11217         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11218         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11219
11220         change->actual_trigger_ce_value = CustomValue[xx][yy];
11221         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11222       }
11223
11224       if (change->can_change && !change_done)
11225       {
11226         ChangeDelay[x][y] = 1;
11227         ChangeEvent[x][y] = trigger_event;
11228
11229         HandleElementChange(x, y, p);
11230
11231         change_done = TRUE;
11232       }
11233       else if (change->has_action)
11234       {
11235         ExecuteCustomElementAction(x, y, element, p);
11236         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11237       }
11238     }
11239   }
11240
11241   RECURSION_LOOP_DETECTION_END();
11242
11243   return change_done;
11244 }
11245
11246 static void PlayPlayerSound(struct PlayerInfo *player)
11247 {
11248   int jx = player->jx, jy = player->jy;
11249   int sound_element = player->artwork_element;
11250   int last_action = player->last_action_waiting;
11251   int action = player->action_waiting;
11252
11253   if (player->is_waiting)
11254   {
11255     if (action != last_action)
11256       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11257     else
11258       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11259   }
11260   else
11261   {
11262     if (action != last_action)
11263       StopSound(element_info[sound_element].sound[last_action]);
11264
11265     if (last_action == ACTION_SLEEPING)
11266       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11267   }
11268 }
11269
11270 static void PlayAllPlayersSound(void)
11271 {
11272   int i;
11273
11274   for (i = 0; i < MAX_PLAYERS; i++)
11275     if (stored_player[i].active)
11276       PlayPlayerSound(&stored_player[i]);
11277 }
11278
11279 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11280 {
11281   boolean last_waiting = player->is_waiting;
11282   int move_dir = player->MovDir;
11283
11284   player->dir_waiting = move_dir;
11285   player->last_action_waiting = player->action_waiting;
11286
11287   if (is_waiting)
11288   {
11289     if (!last_waiting)          // not waiting -> waiting
11290     {
11291       player->is_waiting = TRUE;
11292
11293       player->frame_counter_bored =
11294         FrameCounter +
11295         game.player_boring_delay_fixed +
11296         GetSimpleRandom(game.player_boring_delay_random);
11297       player->frame_counter_sleeping =
11298         FrameCounter +
11299         game.player_sleeping_delay_fixed +
11300         GetSimpleRandom(game.player_sleeping_delay_random);
11301
11302       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11303     }
11304
11305     if (game.player_sleeping_delay_fixed +
11306         game.player_sleeping_delay_random > 0 &&
11307         player->anim_delay_counter == 0 &&
11308         player->post_delay_counter == 0 &&
11309         FrameCounter >= player->frame_counter_sleeping)
11310       player->is_sleeping = TRUE;
11311     else if (game.player_boring_delay_fixed +
11312              game.player_boring_delay_random > 0 &&
11313              FrameCounter >= player->frame_counter_bored)
11314       player->is_bored = TRUE;
11315
11316     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11317                               player->is_bored ? ACTION_BORING :
11318                               ACTION_WAITING);
11319
11320     if (player->is_sleeping && player->use_murphy)
11321     {
11322       // special case for sleeping Murphy when leaning against non-free tile
11323
11324       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11325           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11326            !IS_MOVING(player->jx - 1, player->jy)))
11327         move_dir = MV_LEFT;
11328       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11329                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11330                 !IS_MOVING(player->jx + 1, player->jy)))
11331         move_dir = MV_RIGHT;
11332       else
11333         player->is_sleeping = FALSE;
11334
11335       player->dir_waiting = move_dir;
11336     }
11337
11338     if (player->is_sleeping)
11339     {
11340       if (player->num_special_action_sleeping > 0)
11341       {
11342         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11343         {
11344           int last_special_action = player->special_action_sleeping;
11345           int num_special_action = player->num_special_action_sleeping;
11346           int special_action =
11347             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11348              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11349              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11350              last_special_action + 1 : ACTION_SLEEPING);
11351           int special_graphic =
11352             el_act_dir2img(player->artwork_element, special_action, move_dir);
11353
11354           player->anim_delay_counter =
11355             graphic_info[special_graphic].anim_delay_fixed +
11356             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11357           player->post_delay_counter =
11358             graphic_info[special_graphic].post_delay_fixed +
11359             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11360
11361           player->special_action_sleeping = special_action;
11362         }
11363
11364         if (player->anim_delay_counter > 0)
11365         {
11366           player->action_waiting = player->special_action_sleeping;
11367           player->anim_delay_counter--;
11368         }
11369         else if (player->post_delay_counter > 0)
11370         {
11371           player->post_delay_counter--;
11372         }
11373       }
11374     }
11375     else if (player->is_bored)
11376     {
11377       if (player->num_special_action_bored > 0)
11378       {
11379         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11380         {
11381           int special_action =
11382             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11383           int special_graphic =
11384             el_act_dir2img(player->artwork_element, special_action, move_dir);
11385
11386           player->anim_delay_counter =
11387             graphic_info[special_graphic].anim_delay_fixed +
11388             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11389           player->post_delay_counter =
11390             graphic_info[special_graphic].post_delay_fixed +
11391             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11392
11393           player->special_action_bored = special_action;
11394         }
11395
11396         if (player->anim_delay_counter > 0)
11397         {
11398           player->action_waiting = player->special_action_bored;
11399           player->anim_delay_counter--;
11400         }
11401         else if (player->post_delay_counter > 0)
11402         {
11403           player->post_delay_counter--;
11404         }
11405       }
11406     }
11407   }
11408   else if (last_waiting)        // waiting -> not waiting
11409   {
11410     player->is_waiting = FALSE;
11411     player->is_bored = FALSE;
11412     player->is_sleeping = FALSE;
11413
11414     player->frame_counter_bored = -1;
11415     player->frame_counter_sleeping = -1;
11416
11417     player->anim_delay_counter = 0;
11418     player->post_delay_counter = 0;
11419
11420     player->dir_waiting = player->MovDir;
11421     player->action_waiting = ACTION_DEFAULT;
11422
11423     player->special_action_bored = ACTION_DEFAULT;
11424     player->special_action_sleeping = ACTION_DEFAULT;
11425   }
11426 }
11427
11428 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11429 {
11430   if ((!player->is_moving  && player->was_moving) ||
11431       (player->MovPos == 0 && player->was_moving) ||
11432       (player->is_snapping && !player->was_snapping) ||
11433       (player->is_dropping && !player->was_dropping))
11434   {
11435     if (!CheckSaveEngineSnapshotToList())
11436       return;
11437
11438     player->was_moving = FALSE;
11439     player->was_snapping = TRUE;
11440     player->was_dropping = TRUE;
11441   }
11442   else
11443   {
11444     if (player->is_moving)
11445       player->was_moving = TRUE;
11446
11447     if (!player->is_snapping)
11448       player->was_snapping = FALSE;
11449
11450     if (!player->is_dropping)
11451       player->was_dropping = FALSE;
11452   }
11453
11454   static struct MouseActionInfo mouse_action_last = { 0 };
11455   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11456   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11457
11458   if (new_released)
11459     CheckSaveEngineSnapshotToList();
11460
11461   mouse_action_last = mouse_action;
11462 }
11463
11464 static void CheckSingleStepMode(struct PlayerInfo *player)
11465 {
11466   if (tape.single_step && tape.recording && !tape.pausing)
11467   {
11468     // as it is called "single step mode", just return to pause mode when the
11469     // player stopped moving after one tile (or never starts moving at all)
11470     // (reverse logic needed here in case single step mode used in team mode)
11471     if (player->is_moving ||
11472         player->is_pushing ||
11473         player->is_dropping_pressed ||
11474         player->effective_mouse_action.button)
11475       game.enter_single_step_mode = FALSE;
11476   }
11477
11478   CheckSaveEngineSnapshot(player);
11479 }
11480
11481 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11482 {
11483   int left      = player_action & JOY_LEFT;
11484   int right     = player_action & JOY_RIGHT;
11485   int up        = player_action & JOY_UP;
11486   int down      = player_action & JOY_DOWN;
11487   int button1   = player_action & JOY_BUTTON_1;
11488   int button2   = player_action & JOY_BUTTON_2;
11489   int dx        = (left ? -1 : right ? 1 : 0);
11490   int dy        = (up   ? -1 : down  ? 1 : 0);
11491
11492   if (!player->active || tape.pausing)
11493     return 0;
11494
11495   if (player_action)
11496   {
11497     if (button1)
11498       SnapField(player, dx, dy);
11499     else
11500     {
11501       if (button2)
11502         DropElement(player);
11503
11504       MovePlayer(player, dx, dy);
11505     }
11506
11507     CheckSingleStepMode(player);
11508
11509     SetPlayerWaiting(player, FALSE);
11510
11511     return player_action;
11512   }
11513   else
11514   {
11515     // no actions for this player (no input at player's configured device)
11516
11517     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11518     SnapField(player, 0, 0);
11519     CheckGravityMovementWhenNotMoving(player);
11520
11521     if (player->MovPos == 0)
11522       SetPlayerWaiting(player, TRUE);
11523
11524     if (player->MovPos == 0)    // needed for tape.playing
11525       player->is_moving = FALSE;
11526
11527     player->is_dropping = FALSE;
11528     player->is_dropping_pressed = FALSE;
11529     player->drop_pressed_delay = 0;
11530
11531     CheckSingleStepMode(player);
11532
11533     return 0;
11534   }
11535 }
11536
11537 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11538                                          byte *tape_action)
11539 {
11540   if (!tape.use_mouse_actions)
11541     return;
11542
11543   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11544   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11545   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11546 }
11547
11548 static void SetTapeActionFromMouseAction(byte *tape_action,
11549                                          struct MouseActionInfo *mouse_action)
11550 {
11551   if (!tape.use_mouse_actions)
11552     return;
11553
11554   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11555   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11556   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11557 }
11558
11559 static void CheckLevelSolved(void)
11560 {
11561   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11562   {
11563     if (game_em.level_solved &&
11564         !game_em.game_over)                             // game won
11565     {
11566       LevelSolved();
11567
11568       game_em.game_over = TRUE;
11569
11570       game.all_players_gone = TRUE;
11571     }
11572
11573     if (game_em.game_over)                              // game lost
11574       game.all_players_gone = TRUE;
11575   }
11576   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11577   {
11578     if (game_sp.level_solved &&
11579         !game_sp.game_over)                             // game won
11580     {
11581       LevelSolved();
11582
11583       game_sp.game_over = TRUE;
11584
11585       game.all_players_gone = TRUE;
11586     }
11587
11588     if (game_sp.game_over)                              // game lost
11589       game.all_players_gone = TRUE;
11590   }
11591   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11592   {
11593     if (game_mm.level_solved &&
11594         !game_mm.game_over)                             // game won
11595     {
11596       LevelSolved();
11597
11598       game_mm.game_over = TRUE;
11599
11600       game.all_players_gone = TRUE;
11601     }
11602
11603     if (game_mm.game_over)                              // game lost
11604       game.all_players_gone = TRUE;
11605   }
11606 }
11607
11608 static void CheckLevelTime_StepCounter(void)
11609 {
11610   int i;
11611
11612   TimePlayed++;
11613
11614   if (TimeLeft > 0)
11615   {
11616     TimeLeft--;
11617
11618     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11619       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11620
11621     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11622
11623     DisplayGameControlValues();
11624
11625     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11626       for (i = 0; i < MAX_PLAYERS; i++)
11627         KillPlayer(&stored_player[i]);
11628   }
11629   else if (game.no_level_time_limit && !game.all_players_gone)
11630   {
11631     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11632
11633     DisplayGameControlValues();
11634   }
11635 }
11636
11637 static void CheckLevelTime(void)
11638 {
11639   int i;
11640
11641   if (TimeFrames >= FRAMES_PER_SECOND)
11642   {
11643     TimeFrames = 0;
11644     TapeTime++;
11645
11646     for (i = 0; i < MAX_PLAYERS; i++)
11647     {
11648       struct PlayerInfo *player = &stored_player[i];
11649
11650       if (SHIELD_ON(player))
11651       {
11652         player->shield_normal_time_left--;
11653
11654         if (player->shield_deadly_time_left > 0)
11655           player->shield_deadly_time_left--;
11656       }
11657     }
11658
11659     if (!game.LevelSolved && !level.use_step_counter)
11660     {
11661       TimePlayed++;
11662
11663       if (TimeLeft > 0)
11664       {
11665         TimeLeft--;
11666
11667         if (TimeLeft <= 10 && game.time_limit)
11668           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11669
11670         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11671            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11672
11673         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11674
11675         if (!TimeLeft && game.time_limit)
11676         {
11677           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11678             game_em.lev->killed_out_of_time = TRUE;
11679           else
11680             for (i = 0; i < MAX_PLAYERS; i++)
11681               KillPlayer(&stored_player[i]);
11682         }
11683       }
11684       else if (game.no_level_time_limit && !game.all_players_gone)
11685       {
11686         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11687       }
11688
11689       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11690     }
11691
11692     if (tape.recording || tape.playing)
11693       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11694   }
11695
11696   if (tape.recording || tape.playing)
11697     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11698
11699   UpdateAndDisplayGameControlValues();
11700 }
11701
11702 void AdvanceFrameAndPlayerCounters(int player_nr)
11703 {
11704   int i;
11705
11706   // advance frame counters (global frame counter and time frame counter)
11707   FrameCounter++;
11708   TimeFrames++;
11709
11710   // advance player counters (counters for move delay, move animation etc.)
11711   for (i = 0; i < MAX_PLAYERS; i++)
11712   {
11713     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11714     int move_delay_value = stored_player[i].move_delay_value;
11715     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11716
11717     if (!advance_player_counters)       // not all players may be affected
11718       continue;
11719
11720     if (move_frames == 0)       // less than one move per game frame
11721     {
11722       int stepsize = TILEX / move_delay_value;
11723       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11724       int count = (stored_player[i].is_moving ?
11725                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11726
11727       if (count % delay == 0)
11728         move_frames = 1;
11729     }
11730
11731     stored_player[i].Frame += move_frames;
11732
11733     if (stored_player[i].MovPos != 0)
11734       stored_player[i].StepFrame += move_frames;
11735
11736     if (stored_player[i].move_delay > 0)
11737       stored_player[i].move_delay--;
11738
11739     // due to bugs in previous versions, counter must count up, not down
11740     if (stored_player[i].push_delay != -1)
11741       stored_player[i].push_delay++;
11742
11743     if (stored_player[i].drop_delay > 0)
11744       stored_player[i].drop_delay--;
11745
11746     if (stored_player[i].is_dropping_pressed)
11747       stored_player[i].drop_pressed_delay++;
11748   }
11749 }
11750
11751 void StartGameActions(boolean init_network_game, boolean record_tape,
11752                       int random_seed)
11753 {
11754   unsigned int new_random_seed = InitRND(random_seed);
11755
11756   if (record_tape)
11757     TapeStartRecording(new_random_seed);
11758
11759   if (setup.auto_pause_on_start && !tape.pausing)
11760     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11761
11762   if (init_network_game)
11763   {
11764     SendToServer_LevelFile();
11765     SendToServer_StartPlaying();
11766
11767     return;
11768   }
11769
11770   InitGame();
11771 }
11772
11773 static void GameActionsExt(void)
11774 {
11775 #if 0
11776   static unsigned int game_frame_delay = 0;
11777 #endif
11778   unsigned int game_frame_delay_value;
11779   byte *recorded_player_action;
11780   byte summarized_player_action = 0;
11781   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11782   int i;
11783
11784   // detect endless loops, caused by custom element programming
11785   if (recursion_loop_detected && recursion_loop_depth == 0)
11786   {
11787     char *message = getStringCat3("Internal Error! Element ",
11788                                   EL_NAME(recursion_loop_element),
11789                                   " caused endless loop! Quit the game?");
11790
11791     Warn("element '%s' caused endless loop in game engine",
11792          EL_NAME(recursion_loop_element));
11793
11794     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11795
11796     recursion_loop_detected = FALSE;    // if game should be continued
11797
11798     free(message);
11799
11800     return;
11801   }
11802
11803   if (game.restart_level)
11804     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11805
11806   CheckLevelSolved();
11807
11808   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11809     GameWon();
11810
11811   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11812     TapeStop();
11813
11814   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11815     return;
11816
11817   game_frame_delay_value =
11818     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11819
11820   if (tape.playing && tape.warp_forward && !tape.pausing)
11821     game_frame_delay_value = 0;
11822
11823   SetVideoFrameDelay(game_frame_delay_value);
11824
11825   // (de)activate virtual buttons depending on current game status
11826   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11827   {
11828     if (game.all_players_gone)  // if no players there to be controlled anymore
11829       SetOverlayActive(FALSE);
11830     else if (!tape.playing)     // if game continues after tape stopped playing
11831       SetOverlayActive(TRUE);
11832   }
11833
11834 #if 0
11835 #if 0
11836   // ---------- main game synchronization point ----------
11837
11838   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11839
11840   Debug("game:playing:skip", "skip == %d", skip);
11841
11842 #else
11843   // ---------- main game synchronization point ----------
11844
11845   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11846 #endif
11847 #endif
11848
11849   if (network_playing && !network_player_action_received)
11850   {
11851     // try to get network player actions in time
11852
11853     // last chance to get network player actions without main loop delay
11854     HandleNetworking();
11855
11856     // game was quit by network peer
11857     if (game_status != GAME_MODE_PLAYING)
11858       return;
11859
11860     // check if network player actions still missing and game still running
11861     if (!network_player_action_received && !checkGameEnded())
11862       return;           // failed to get network player actions in time
11863
11864     // do not yet reset "network_player_action_received" (for tape.pausing)
11865   }
11866
11867   if (tape.pausing)
11868     return;
11869
11870   // at this point we know that we really continue executing the game
11871
11872   network_player_action_received = FALSE;
11873
11874   // when playing tape, read previously recorded player input from tape data
11875   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11876
11877   local_player->effective_mouse_action = local_player->mouse_action;
11878
11879   if (recorded_player_action != NULL)
11880     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11881                                  recorded_player_action);
11882
11883   // TapePlayAction() may return NULL when toggling to "pause before death"
11884   if (tape.pausing)
11885     return;
11886
11887   if (tape.set_centered_player)
11888   {
11889     game.centered_player_nr_next = tape.centered_player_nr_next;
11890     game.set_centered_player = TRUE;
11891   }
11892
11893   for (i = 0; i < MAX_PLAYERS; i++)
11894   {
11895     summarized_player_action |= stored_player[i].action;
11896
11897     if (!network_playing && (game.team_mode || tape.playing))
11898       stored_player[i].effective_action = stored_player[i].action;
11899   }
11900
11901   if (network_playing && !checkGameEnded())
11902     SendToServer_MovePlayer(summarized_player_action);
11903
11904   // summarize all actions at local players mapped input device position
11905   // (this allows using different input devices in single player mode)
11906   if (!network.enabled && !game.team_mode)
11907     stored_player[map_player_action[local_player->index_nr]].effective_action =
11908       summarized_player_action;
11909
11910   // summarize all actions at centered player in local team mode
11911   if (tape.recording &&
11912       setup.team_mode && !network.enabled &&
11913       setup.input_on_focus &&
11914       game.centered_player_nr != -1)
11915   {
11916     for (i = 0; i < MAX_PLAYERS; i++)
11917       stored_player[map_player_action[i]].effective_action =
11918         (i == game.centered_player_nr ? summarized_player_action : 0);
11919   }
11920
11921   if (recorded_player_action != NULL)
11922     for (i = 0; i < MAX_PLAYERS; i++)
11923       stored_player[i].effective_action = recorded_player_action[i];
11924
11925   for (i = 0; i < MAX_PLAYERS; i++)
11926   {
11927     tape_action[i] = stored_player[i].effective_action;
11928
11929     /* (this may happen in the RND game engine if a player was not present on
11930        the playfield on level start, but appeared later from a custom element */
11931     if (setup.team_mode &&
11932         tape.recording &&
11933         tape_action[i] &&
11934         !tape.player_participates[i])
11935       tape.player_participates[i] = TRUE;
11936   }
11937
11938   SetTapeActionFromMouseAction(tape_action,
11939                                &local_player->effective_mouse_action);
11940
11941   // only record actions from input devices, but not programmed actions
11942   if (tape.recording)
11943     TapeRecordAction(tape_action);
11944
11945   // remember if game was played (especially after tape stopped playing)
11946   if (!tape.playing && summarized_player_action)
11947     game.GamePlayed = TRUE;
11948
11949 #if USE_NEW_PLAYER_ASSIGNMENTS
11950   // !!! also map player actions in single player mode !!!
11951   // if (game.team_mode)
11952   if (1)
11953   {
11954     byte mapped_action[MAX_PLAYERS];
11955
11956 #if DEBUG_PLAYER_ACTIONS
11957     for (i = 0; i < MAX_PLAYERS; i++)
11958       DebugContinued("", "%d, ", stored_player[i].effective_action);
11959 #endif
11960
11961     for (i = 0; i < MAX_PLAYERS; i++)
11962       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11963
11964     for (i = 0; i < MAX_PLAYERS; i++)
11965       stored_player[i].effective_action = mapped_action[i];
11966
11967 #if DEBUG_PLAYER_ACTIONS
11968     DebugContinued("", "=> ");
11969     for (i = 0; i < MAX_PLAYERS; i++)
11970       DebugContinued("", "%d, ", stored_player[i].effective_action);
11971     DebugContinued("game:playing:player", "\n");
11972 #endif
11973   }
11974 #if DEBUG_PLAYER_ACTIONS
11975   else
11976   {
11977     for (i = 0; i < MAX_PLAYERS; i++)
11978       DebugContinued("", "%d, ", stored_player[i].effective_action);
11979     DebugContinued("game:playing:player", "\n");
11980   }
11981 #endif
11982 #endif
11983
11984   for (i = 0; i < MAX_PLAYERS; i++)
11985   {
11986     // allow engine snapshot in case of changed movement attempt
11987     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11988         (stored_player[i].effective_action & KEY_MOTION))
11989       game.snapshot.changed_action = TRUE;
11990
11991     // allow engine snapshot in case of snapping/dropping attempt
11992     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11993         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11994       game.snapshot.changed_action = TRUE;
11995
11996     game.snapshot.last_action[i] = stored_player[i].effective_action;
11997   }
11998
11999   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12000   {
12001     GameActions_EM_Main();
12002   }
12003   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12004   {
12005     GameActions_SP_Main();
12006   }
12007   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12008   {
12009     GameActions_MM_Main();
12010   }
12011   else
12012   {
12013     GameActions_RND_Main();
12014   }
12015
12016   BlitScreenToBitmap(backbuffer);
12017
12018   CheckLevelSolved();
12019   CheckLevelTime();
12020
12021   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12022
12023   if (global.show_frames_per_second)
12024   {
12025     static unsigned int fps_counter = 0;
12026     static int fps_frames = 0;
12027     unsigned int fps_delay_ms = Counter() - fps_counter;
12028
12029     fps_frames++;
12030
12031     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12032     {
12033       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12034
12035       fps_frames = 0;
12036       fps_counter = Counter();
12037
12038       // always draw FPS to screen after FPS value was updated
12039       redraw_mask |= REDRAW_FPS;
12040     }
12041
12042     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12043     if (GetDrawDeactivationMask() == REDRAW_NONE)
12044       redraw_mask |= REDRAW_FPS;
12045   }
12046 }
12047
12048 static void GameActions_CheckSaveEngineSnapshot(void)
12049 {
12050   if (!game.snapshot.save_snapshot)
12051     return;
12052
12053   // clear flag for saving snapshot _before_ saving snapshot
12054   game.snapshot.save_snapshot = FALSE;
12055
12056   SaveEngineSnapshotToList();
12057 }
12058
12059 void GameActions(void)
12060 {
12061   GameActionsExt();
12062
12063   GameActions_CheckSaveEngineSnapshot();
12064 }
12065
12066 void GameActions_EM_Main(void)
12067 {
12068   byte effective_action[MAX_PLAYERS];
12069   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12070   int i;
12071
12072   for (i = 0; i < MAX_PLAYERS; i++)
12073     effective_action[i] = stored_player[i].effective_action;
12074
12075   GameActions_EM(effective_action, warp_mode);
12076 }
12077
12078 void GameActions_SP_Main(void)
12079 {
12080   byte effective_action[MAX_PLAYERS];
12081   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12082   int i;
12083
12084   for (i = 0; i < MAX_PLAYERS; i++)
12085     effective_action[i] = stored_player[i].effective_action;
12086
12087   GameActions_SP(effective_action, warp_mode);
12088
12089   for (i = 0; i < MAX_PLAYERS; i++)
12090   {
12091     if (stored_player[i].force_dropping)
12092       stored_player[i].action |= KEY_BUTTON_DROP;
12093
12094     stored_player[i].force_dropping = FALSE;
12095   }
12096 }
12097
12098 void GameActions_MM_Main(void)
12099 {
12100   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12101
12102   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12103 }
12104
12105 void GameActions_RND_Main(void)
12106 {
12107   GameActions_RND();
12108 }
12109
12110 void GameActions_RND(void)
12111 {
12112   static struct MouseActionInfo mouse_action_last = { 0 };
12113   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12114   int magic_wall_x = 0, magic_wall_y = 0;
12115   int i, x, y, element, graphic, last_gfx_frame;
12116
12117   InitPlayfieldScanModeVars();
12118
12119   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12120   {
12121     SCAN_PLAYFIELD(x, y)
12122     {
12123       ChangeCount[x][y] = 0;
12124       ChangeEvent[x][y] = -1;
12125     }
12126   }
12127
12128   if (game.set_centered_player)
12129   {
12130     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12131
12132     // switching to "all players" only possible if all players fit to screen
12133     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12134     {
12135       game.centered_player_nr_next = game.centered_player_nr;
12136       game.set_centered_player = FALSE;
12137     }
12138
12139     // do not switch focus to non-existing (or non-active) player
12140     if (game.centered_player_nr_next >= 0 &&
12141         !stored_player[game.centered_player_nr_next].active)
12142     {
12143       game.centered_player_nr_next = game.centered_player_nr;
12144       game.set_centered_player = FALSE;
12145     }
12146   }
12147
12148   if (game.set_centered_player &&
12149       ScreenMovPos == 0)        // screen currently aligned at tile position
12150   {
12151     int sx, sy;
12152
12153     if (game.centered_player_nr_next == -1)
12154     {
12155       setScreenCenteredToAllPlayers(&sx, &sy);
12156     }
12157     else
12158     {
12159       sx = stored_player[game.centered_player_nr_next].jx;
12160       sy = stored_player[game.centered_player_nr_next].jy;
12161     }
12162
12163     game.centered_player_nr = game.centered_player_nr_next;
12164     game.set_centered_player = FALSE;
12165
12166     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12167     DrawGameDoorValues();
12168   }
12169
12170   // check single step mode (set flag and clear again if any player is active)
12171   game.enter_single_step_mode =
12172     (tape.single_step && tape.recording && !tape.pausing);
12173
12174   for (i = 0; i < MAX_PLAYERS; i++)
12175   {
12176     int actual_player_action = stored_player[i].effective_action;
12177
12178 #if 1
12179     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12180        - rnd_equinox_tetrachloride 048
12181        - rnd_equinox_tetrachloride_ii 096
12182        - rnd_emanuel_schmieg 002
12183        - doctor_sloan_ww 001, 020
12184     */
12185     if (stored_player[i].MovPos == 0)
12186       CheckGravityMovement(&stored_player[i]);
12187 #endif
12188
12189     // overwrite programmed action with tape action
12190     if (stored_player[i].programmed_action)
12191       actual_player_action = stored_player[i].programmed_action;
12192
12193     PlayerActions(&stored_player[i], actual_player_action);
12194
12195     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12196   }
12197
12198   // single step pause mode may already have been toggled by "ScrollPlayer()"
12199   if (game.enter_single_step_mode && !tape.pausing)
12200     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12201
12202   ScrollScreen(NULL, SCROLL_GO_ON);
12203
12204   /* for backwards compatibility, the following code emulates a fixed bug that
12205      occured when pushing elements (causing elements that just made their last
12206      pushing step to already (if possible) make their first falling step in the
12207      same game frame, which is bad); this code is also needed to use the famous
12208      "spring push bug" which is used in older levels and might be wanted to be
12209      used also in newer levels, but in this case the buggy pushing code is only
12210      affecting the "spring" element and no other elements */
12211
12212   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12213   {
12214     for (i = 0; i < MAX_PLAYERS; i++)
12215     {
12216       struct PlayerInfo *player = &stored_player[i];
12217       int x = player->jx;
12218       int y = player->jy;
12219
12220       if (player->active && player->is_pushing && player->is_moving &&
12221           IS_MOVING(x, y) &&
12222           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12223            Tile[x][y] == EL_SPRING))
12224       {
12225         ContinueMoving(x, y);
12226
12227         // continue moving after pushing (this is actually a bug)
12228         if (!IS_MOVING(x, y))
12229           Stop[x][y] = FALSE;
12230       }
12231     }
12232   }
12233
12234   SCAN_PLAYFIELD(x, y)
12235   {
12236     Last[x][y] = Tile[x][y];
12237
12238     ChangeCount[x][y] = 0;
12239     ChangeEvent[x][y] = -1;
12240
12241     // this must be handled before main playfield loop
12242     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12243     {
12244       MovDelay[x][y]--;
12245       if (MovDelay[x][y] <= 0)
12246         RemoveField(x, y);
12247     }
12248
12249     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12250     {
12251       MovDelay[x][y]--;
12252       if (MovDelay[x][y] <= 0)
12253       {
12254         int element = Store[x][y];
12255         int move_direction = MovDir[x][y];
12256         int player_index_bit = Store2[x][y];
12257
12258         Store[x][y] = 0;
12259         Store2[x][y] = 0;
12260
12261         RemoveField(x, y);
12262         TEST_DrawLevelField(x, y);
12263
12264         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12265
12266         if (IS_ENVELOPE(element))
12267           local_player->show_envelope = element;
12268       }
12269     }
12270
12271 #if DEBUG
12272     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12273     {
12274       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12275             x, y);
12276       Debug("game:playing:GameActions_RND", "This should never happen!");
12277
12278       ChangePage[x][y] = -1;
12279     }
12280 #endif
12281
12282     Stop[x][y] = FALSE;
12283     if (WasJustMoving[x][y] > 0)
12284       WasJustMoving[x][y]--;
12285     if (WasJustFalling[x][y] > 0)
12286       WasJustFalling[x][y]--;
12287     if (CheckCollision[x][y] > 0)
12288       CheckCollision[x][y]--;
12289     if (CheckImpact[x][y] > 0)
12290       CheckImpact[x][y]--;
12291
12292     GfxFrame[x][y]++;
12293
12294     /* reset finished pushing action (not done in ContinueMoving() to allow
12295        continuous pushing animation for elements with zero push delay) */
12296     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12297     {
12298       ResetGfxAnimation(x, y);
12299       TEST_DrawLevelField(x, y);
12300     }
12301
12302 #if DEBUG
12303     if (IS_BLOCKED(x, y))
12304     {
12305       int oldx, oldy;
12306
12307       Blocked2Moving(x, y, &oldx, &oldy);
12308       if (!IS_MOVING(oldx, oldy))
12309       {
12310         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12311         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12312         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12313         Debug("game:playing:GameActions_RND", "This should never happen!");
12314       }
12315     }
12316 #endif
12317   }
12318
12319   if (mouse_action.button)
12320   {
12321     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12322     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12323
12324     x = mouse_action.lx;
12325     y = mouse_action.ly;
12326     element = Tile[x][y];
12327
12328     if (new_button)
12329     {
12330       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12331       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12332                                          ch_button);
12333     }
12334
12335     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12336     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12337                                        ch_button);
12338
12339     if (level.use_step_counter)
12340     {
12341       boolean counted_click = FALSE;
12342
12343       // element clicked that can change when clicked/pressed
12344       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12345           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12346            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12347         counted_click = TRUE;
12348
12349       // element clicked that can trigger change when clicked/pressed
12350       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12351           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12352         counted_click = TRUE;
12353
12354       if (new_button && counted_click)
12355         CheckLevelTime_StepCounter();
12356     }
12357   }
12358
12359   SCAN_PLAYFIELD(x, y)
12360   {
12361     element = Tile[x][y];
12362     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12363     last_gfx_frame = GfxFrame[x][y];
12364
12365     if (element == EL_EMPTY)
12366       graphic = el2img(GfxElementEmpty[x][y]);
12367
12368     ResetGfxFrame(x, y);
12369
12370     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12371       DrawLevelGraphicAnimation(x, y, graphic);
12372
12373     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12374         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12375       ResetRandomAnimationValue(x, y);
12376
12377     SetRandomAnimationValue(x, y);
12378
12379     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12380
12381     if (IS_INACTIVE(element))
12382     {
12383       if (IS_ANIMATED(graphic))
12384         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12385
12386       continue;
12387     }
12388
12389     // this may take place after moving, so 'element' may have changed
12390     if (IS_CHANGING(x, y) &&
12391         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12392     {
12393       int page = element_info[element].event_page_nr[CE_DELAY];
12394
12395       HandleElementChange(x, y, page);
12396
12397       element = Tile[x][y];
12398       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12399     }
12400
12401     CheckNextToConditions(x, y);
12402
12403     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12404     {
12405       StartMoving(x, y);
12406
12407       element = Tile[x][y];
12408       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12409
12410       if (IS_ANIMATED(graphic) &&
12411           !IS_MOVING(x, y) &&
12412           !Stop[x][y])
12413         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12414
12415       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12416         TEST_DrawTwinkleOnField(x, y);
12417     }
12418     else if (element == EL_ACID)
12419     {
12420       if (!Stop[x][y])
12421         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12422     }
12423     else if ((element == EL_EXIT_OPEN ||
12424               element == EL_EM_EXIT_OPEN ||
12425               element == EL_SP_EXIT_OPEN ||
12426               element == EL_STEEL_EXIT_OPEN ||
12427               element == EL_EM_STEEL_EXIT_OPEN ||
12428               element == EL_SP_TERMINAL ||
12429               element == EL_SP_TERMINAL_ACTIVE ||
12430               element == EL_EXTRA_TIME ||
12431               element == EL_SHIELD_NORMAL ||
12432               element == EL_SHIELD_DEADLY) &&
12433              IS_ANIMATED(graphic))
12434       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12435     else if (IS_MOVING(x, y))
12436       ContinueMoving(x, y);
12437     else if (IS_ACTIVE_BOMB(element))
12438       CheckDynamite(x, y);
12439     else if (element == EL_AMOEBA_GROWING)
12440       AmoebaGrowing(x, y);
12441     else if (element == EL_AMOEBA_SHRINKING)
12442       AmoebaShrinking(x, y);
12443
12444 #if !USE_NEW_AMOEBA_CODE
12445     else if (IS_AMOEBALIVE(element))
12446       AmoebaReproduce(x, y);
12447 #endif
12448
12449     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12450       Life(x, y);
12451     else if (element == EL_EXIT_CLOSED)
12452       CheckExit(x, y);
12453     else if (element == EL_EM_EXIT_CLOSED)
12454       CheckExitEM(x, y);
12455     else if (element == EL_STEEL_EXIT_CLOSED)
12456       CheckExitSteel(x, y);
12457     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12458       CheckExitSteelEM(x, y);
12459     else if (element == EL_SP_EXIT_CLOSED)
12460       CheckExitSP(x, y);
12461     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12462              element == EL_EXPANDABLE_STEELWALL_GROWING)
12463       WallGrowing(x, y);
12464     else if (element == EL_EXPANDABLE_WALL ||
12465              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12466              element == EL_EXPANDABLE_WALL_VERTICAL ||
12467              element == EL_EXPANDABLE_WALL_ANY ||
12468              element == EL_BD_EXPANDABLE_WALL)
12469       CheckWallGrowing(x, y);
12470     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12471              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12472              element == EL_EXPANDABLE_STEELWALL_ANY)
12473       CheckSteelWallGrowing(x, y);
12474     else if (element == EL_FLAMES)
12475       CheckForDragon(x, y);
12476     else if (element == EL_EXPLOSION)
12477       ; // drawing of correct explosion animation is handled separately
12478     else if (element == EL_ELEMENT_SNAPPING ||
12479              element == EL_DIAGONAL_SHRINKING ||
12480              element == EL_DIAGONAL_GROWING)
12481     {
12482       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12483
12484       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12485     }
12486     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12487       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12488
12489     if (IS_BELT_ACTIVE(element))
12490       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12491
12492     if (game.magic_wall_active)
12493     {
12494       int jx = local_player->jx, jy = local_player->jy;
12495
12496       // play the element sound at the position nearest to the player
12497       if ((element == EL_MAGIC_WALL_FULL ||
12498            element == EL_MAGIC_WALL_ACTIVE ||
12499            element == EL_MAGIC_WALL_EMPTYING ||
12500            element == EL_BD_MAGIC_WALL_FULL ||
12501            element == EL_BD_MAGIC_WALL_ACTIVE ||
12502            element == EL_BD_MAGIC_WALL_EMPTYING ||
12503            element == EL_DC_MAGIC_WALL_FULL ||
12504            element == EL_DC_MAGIC_WALL_ACTIVE ||
12505            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12506           ABS(x - jx) + ABS(y - jy) <
12507           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12508       {
12509         magic_wall_x = x;
12510         magic_wall_y = y;
12511       }
12512     }
12513   }
12514
12515 #if USE_NEW_AMOEBA_CODE
12516   // new experimental amoeba growth stuff
12517   if (!(FrameCounter % 8))
12518   {
12519     static unsigned int random = 1684108901;
12520
12521     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12522     {
12523       x = RND(lev_fieldx);
12524       y = RND(lev_fieldy);
12525       element = Tile[x][y];
12526
12527       if (!IS_PLAYER(x,y) &&
12528           (element == EL_EMPTY ||
12529            CAN_GROW_INTO(element) ||
12530            element == EL_QUICKSAND_EMPTY ||
12531            element == EL_QUICKSAND_FAST_EMPTY ||
12532            element == EL_ACID_SPLASH_LEFT ||
12533            element == EL_ACID_SPLASH_RIGHT))
12534       {
12535         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12536             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12537             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12538             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12539           Tile[x][y] = EL_AMOEBA_DROP;
12540       }
12541
12542       random = random * 129 + 1;
12543     }
12544   }
12545 #endif
12546
12547   game.explosions_delayed = FALSE;
12548
12549   SCAN_PLAYFIELD(x, y)
12550   {
12551     element = Tile[x][y];
12552
12553     if (ExplodeField[x][y])
12554       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12555     else if (element == EL_EXPLOSION)
12556       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12557
12558     ExplodeField[x][y] = EX_TYPE_NONE;
12559   }
12560
12561   game.explosions_delayed = TRUE;
12562
12563   if (game.magic_wall_active)
12564   {
12565     if (!(game.magic_wall_time_left % 4))
12566     {
12567       int element = Tile[magic_wall_x][magic_wall_y];
12568
12569       if (element == EL_BD_MAGIC_WALL_FULL ||
12570           element == EL_BD_MAGIC_WALL_ACTIVE ||
12571           element == EL_BD_MAGIC_WALL_EMPTYING)
12572         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12573       else if (element == EL_DC_MAGIC_WALL_FULL ||
12574                element == EL_DC_MAGIC_WALL_ACTIVE ||
12575                element == EL_DC_MAGIC_WALL_EMPTYING)
12576         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12577       else
12578         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12579     }
12580
12581     if (game.magic_wall_time_left > 0)
12582     {
12583       game.magic_wall_time_left--;
12584
12585       if (!game.magic_wall_time_left)
12586       {
12587         SCAN_PLAYFIELD(x, y)
12588         {
12589           element = Tile[x][y];
12590
12591           if (element == EL_MAGIC_WALL_ACTIVE ||
12592               element == EL_MAGIC_WALL_FULL)
12593           {
12594             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12595             TEST_DrawLevelField(x, y);
12596           }
12597           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12598                    element == EL_BD_MAGIC_WALL_FULL)
12599           {
12600             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12601             TEST_DrawLevelField(x, y);
12602           }
12603           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12604                    element == EL_DC_MAGIC_WALL_FULL)
12605           {
12606             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12607             TEST_DrawLevelField(x, y);
12608           }
12609         }
12610
12611         game.magic_wall_active = FALSE;
12612       }
12613     }
12614   }
12615
12616   if (game.light_time_left > 0)
12617   {
12618     game.light_time_left--;
12619
12620     if (game.light_time_left == 0)
12621       RedrawAllLightSwitchesAndInvisibleElements();
12622   }
12623
12624   if (game.timegate_time_left > 0)
12625   {
12626     game.timegate_time_left--;
12627
12628     if (game.timegate_time_left == 0)
12629       CloseAllOpenTimegates();
12630   }
12631
12632   if (game.lenses_time_left > 0)
12633   {
12634     game.lenses_time_left--;
12635
12636     if (game.lenses_time_left == 0)
12637       RedrawAllInvisibleElementsForLenses();
12638   }
12639
12640   if (game.magnify_time_left > 0)
12641   {
12642     game.magnify_time_left--;
12643
12644     if (game.magnify_time_left == 0)
12645       RedrawAllInvisibleElementsForMagnifier();
12646   }
12647
12648   for (i = 0; i < MAX_PLAYERS; i++)
12649   {
12650     struct PlayerInfo *player = &stored_player[i];
12651
12652     if (SHIELD_ON(player))
12653     {
12654       if (player->shield_deadly_time_left)
12655         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12656       else if (player->shield_normal_time_left)
12657         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12658     }
12659   }
12660
12661 #if USE_DELAYED_GFX_REDRAW
12662   SCAN_PLAYFIELD(x, y)
12663   {
12664     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12665     {
12666       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12667          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12668
12669       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12670         DrawLevelField(x, y);
12671
12672       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12673         DrawLevelFieldCrumbled(x, y);
12674
12675       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12676         DrawLevelFieldCrumbledNeighbours(x, y);
12677
12678       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12679         DrawTwinkleOnField(x, y);
12680     }
12681
12682     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12683   }
12684 #endif
12685
12686   DrawAllPlayers();
12687   PlayAllPlayersSound();
12688
12689   for (i = 0; i < MAX_PLAYERS; i++)
12690   {
12691     struct PlayerInfo *player = &stored_player[i];
12692
12693     if (player->show_envelope != 0 && (!player->active ||
12694                                        player->MovPos == 0))
12695     {
12696       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12697
12698       player->show_envelope = 0;
12699     }
12700   }
12701
12702   // use random number generator in every frame to make it less predictable
12703   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12704     RND(1);
12705
12706   mouse_action_last = mouse_action;
12707 }
12708
12709 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12710 {
12711   int min_x = x, min_y = y, max_x = x, max_y = y;
12712   int scr_fieldx = getScreenFieldSizeX();
12713   int scr_fieldy = getScreenFieldSizeY();
12714   int i;
12715
12716   for (i = 0; i < MAX_PLAYERS; i++)
12717   {
12718     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12719
12720     if (!stored_player[i].active || &stored_player[i] == player)
12721       continue;
12722
12723     min_x = MIN(min_x, jx);
12724     min_y = MIN(min_y, jy);
12725     max_x = MAX(max_x, jx);
12726     max_y = MAX(max_y, jy);
12727   }
12728
12729   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12730 }
12731
12732 static boolean AllPlayersInVisibleScreen(void)
12733 {
12734   int i;
12735
12736   for (i = 0; i < MAX_PLAYERS; i++)
12737   {
12738     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12739
12740     if (!stored_player[i].active)
12741       continue;
12742
12743     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12744       return FALSE;
12745   }
12746
12747   return TRUE;
12748 }
12749
12750 void ScrollLevel(int dx, int dy)
12751 {
12752   int scroll_offset = 2 * TILEX_VAR;
12753   int x, y;
12754
12755   BlitBitmap(drawto_field, drawto_field,
12756              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12757              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12758              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12759              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12760              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12761              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12762
12763   if (dx != 0)
12764   {
12765     x = (dx == 1 ? BX1 : BX2);
12766     for (y = BY1; y <= BY2; y++)
12767       DrawScreenField(x, y);
12768   }
12769
12770   if (dy != 0)
12771   {
12772     y = (dy == 1 ? BY1 : BY2);
12773     for (x = BX1; x <= BX2; x++)
12774       DrawScreenField(x, y);
12775   }
12776
12777   redraw_mask |= REDRAW_FIELD;
12778 }
12779
12780 static boolean canFallDown(struct PlayerInfo *player)
12781 {
12782   int jx = player->jx, jy = player->jy;
12783
12784   return (IN_LEV_FIELD(jx, jy + 1) &&
12785           (IS_FREE(jx, jy + 1) ||
12786            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12787           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12788           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12789 }
12790
12791 static boolean canPassField(int x, int y, int move_dir)
12792 {
12793   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12794   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12795   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12796   int nextx = x + dx;
12797   int nexty = y + dy;
12798   int element = Tile[x][y];
12799
12800   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12801           !CAN_MOVE(element) &&
12802           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12803           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12804           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12805 }
12806
12807 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12808 {
12809   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12810   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12811   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12812   int newx = x + dx;
12813   int newy = y + dy;
12814
12815   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12816           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12817           (IS_DIGGABLE(Tile[newx][newy]) ||
12818            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12819            canPassField(newx, newy, move_dir)));
12820 }
12821
12822 static void CheckGravityMovement(struct PlayerInfo *player)
12823 {
12824   if (player->gravity && !player->programmed_action)
12825   {
12826     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12827     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12828     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12829     int jx = player->jx, jy = player->jy;
12830     boolean player_is_moving_to_valid_field =
12831       (!player_is_snapping &&
12832        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12833         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12834     boolean player_can_fall_down = canFallDown(player);
12835
12836     if (player_can_fall_down &&
12837         !player_is_moving_to_valid_field)
12838       player->programmed_action = MV_DOWN;
12839   }
12840 }
12841
12842 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12843 {
12844   return CheckGravityMovement(player);
12845
12846   if (player->gravity && !player->programmed_action)
12847   {
12848     int jx = player->jx, jy = player->jy;
12849     boolean field_under_player_is_free =
12850       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12851     boolean player_is_standing_on_valid_field =
12852       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12853        (IS_WALKABLE(Tile[jx][jy]) &&
12854         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12855
12856     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12857       player->programmed_action = MV_DOWN;
12858   }
12859 }
12860
12861 /*
12862   MovePlayerOneStep()
12863   -----------------------------------------------------------------------------
12864   dx, dy:               direction (non-diagonal) to try to move the player to
12865   real_dx, real_dy:     direction as read from input device (can be diagonal)
12866 */
12867
12868 boolean MovePlayerOneStep(struct PlayerInfo *player,
12869                           int dx, int dy, int real_dx, int real_dy)
12870 {
12871   int jx = player->jx, jy = player->jy;
12872   int new_jx = jx + dx, new_jy = jy + dy;
12873   int can_move;
12874   boolean player_can_move = !player->cannot_move;
12875
12876   if (!player->active || (!dx && !dy))
12877     return MP_NO_ACTION;
12878
12879   player->MovDir = (dx < 0 ? MV_LEFT :
12880                     dx > 0 ? MV_RIGHT :
12881                     dy < 0 ? MV_UP :
12882                     dy > 0 ? MV_DOWN :  MV_NONE);
12883
12884   if (!IN_LEV_FIELD(new_jx, new_jy))
12885     return MP_NO_ACTION;
12886
12887   if (!player_can_move)
12888   {
12889     if (player->MovPos == 0)
12890     {
12891       player->is_moving = FALSE;
12892       player->is_digging = FALSE;
12893       player->is_collecting = FALSE;
12894       player->is_snapping = FALSE;
12895       player->is_pushing = FALSE;
12896     }
12897   }
12898
12899   if (!network.enabled && game.centered_player_nr == -1 &&
12900       !AllPlayersInSight(player, new_jx, new_jy))
12901     return MP_NO_ACTION;
12902
12903   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12904   if (can_move != MP_MOVING)
12905     return can_move;
12906
12907   // check if DigField() has caused relocation of the player
12908   if (player->jx != jx || player->jy != jy)
12909     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12910
12911   StorePlayer[jx][jy] = 0;
12912   player->last_jx = jx;
12913   player->last_jy = jy;
12914   player->jx = new_jx;
12915   player->jy = new_jy;
12916   StorePlayer[new_jx][new_jy] = player->element_nr;
12917
12918   if (player->move_delay_value_next != -1)
12919   {
12920     player->move_delay_value = player->move_delay_value_next;
12921     player->move_delay_value_next = -1;
12922   }
12923
12924   player->MovPos =
12925     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12926
12927   player->step_counter++;
12928
12929   PlayerVisit[jx][jy] = FrameCounter;
12930
12931   player->is_moving = TRUE;
12932
12933 #if 1
12934   // should better be called in MovePlayer(), but this breaks some tapes
12935   ScrollPlayer(player, SCROLL_INIT);
12936 #endif
12937
12938   return MP_MOVING;
12939 }
12940
12941 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12942 {
12943   int jx = player->jx, jy = player->jy;
12944   int old_jx = jx, old_jy = jy;
12945   int moved = MP_NO_ACTION;
12946
12947   if (!player->active)
12948     return FALSE;
12949
12950   if (!dx && !dy)
12951   {
12952     if (player->MovPos == 0)
12953     {
12954       player->is_moving = FALSE;
12955       player->is_digging = FALSE;
12956       player->is_collecting = FALSE;
12957       player->is_snapping = FALSE;
12958       player->is_pushing = FALSE;
12959     }
12960
12961     return FALSE;
12962   }
12963
12964   if (player->move_delay > 0)
12965     return FALSE;
12966
12967   player->move_delay = -1;              // set to "uninitialized" value
12968
12969   // store if player is automatically moved to next field
12970   player->is_auto_moving = (player->programmed_action != MV_NONE);
12971
12972   // remove the last programmed player action
12973   player->programmed_action = 0;
12974
12975   if (player->MovPos)
12976   {
12977     // should only happen if pre-1.2 tape recordings are played
12978     // this is only for backward compatibility
12979
12980     int original_move_delay_value = player->move_delay_value;
12981
12982 #if DEBUG
12983     Debug("game:playing:MovePlayer",
12984           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12985           tape.counter);
12986 #endif
12987
12988     // scroll remaining steps with finest movement resolution
12989     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12990
12991     while (player->MovPos)
12992     {
12993       ScrollPlayer(player, SCROLL_GO_ON);
12994       ScrollScreen(NULL, SCROLL_GO_ON);
12995
12996       AdvanceFrameAndPlayerCounters(player->index_nr);
12997
12998       DrawAllPlayers();
12999       BackToFront_WithFrameDelay(0);
13000     }
13001
13002     player->move_delay_value = original_move_delay_value;
13003   }
13004
13005   player->is_active = FALSE;
13006
13007   if (player->last_move_dir & MV_HORIZONTAL)
13008   {
13009     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13010       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13011   }
13012   else
13013   {
13014     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13015       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13016   }
13017
13018   if (!moved && !player->is_active)
13019   {
13020     player->is_moving = FALSE;
13021     player->is_digging = FALSE;
13022     player->is_collecting = FALSE;
13023     player->is_snapping = FALSE;
13024     player->is_pushing = FALSE;
13025   }
13026
13027   jx = player->jx;
13028   jy = player->jy;
13029
13030   if (moved & MP_MOVING && !ScreenMovPos &&
13031       (player->index_nr == game.centered_player_nr ||
13032        game.centered_player_nr == -1))
13033   {
13034     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13035
13036     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13037     {
13038       // actual player has left the screen -- scroll in that direction
13039       if (jx != old_jx)         // player has moved horizontally
13040         scroll_x += (jx - old_jx);
13041       else                      // player has moved vertically
13042         scroll_y += (jy - old_jy);
13043     }
13044     else
13045     {
13046       int offset_raw = game.scroll_delay_value;
13047
13048       if (jx != old_jx)         // player has moved horizontally
13049       {
13050         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13051         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13052         int new_scroll_x = jx - MIDPOSX + offset_x;
13053
13054         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13055             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13056           scroll_x = new_scroll_x;
13057
13058         // don't scroll over playfield boundaries
13059         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13060
13061         // don't scroll more than one field at a time
13062         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13063
13064         // don't scroll against the player's moving direction
13065         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13066             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13067           scroll_x = old_scroll_x;
13068       }
13069       else                      // player has moved vertically
13070       {
13071         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13072         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13073         int new_scroll_y = jy - MIDPOSY + offset_y;
13074
13075         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13076             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13077           scroll_y = new_scroll_y;
13078
13079         // don't scroll over playfield boundaries
13080         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13081
13082         // don't scroll more than one field at a time
13083         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13084
13085         // don't scroll against the player's moving direction
13086         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13087             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13088           scroll_y = old_scroll_y;
13089       }
13090     }
13091
13092     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13093     {
13094       if (!network.enabled && game.centered_player_nr == -1 &&
13095           !AllPlayersInVisibleScreen())
13096       {
13097         scroll_x = old_scroll_x;
13098         scroll_y = old_scroll_y;
13099       }
13100       else
13101       {
13102         ScrollScreen(player, SCROLL_INIT);
13103         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13104       }
13105     }
13106   }
13107
13108   player->StepFrame = 0;
13109
13110   if (moved & MP_MOVING)
13111   {
13112     if (old_jx != jx && old_jy == jy)
13113       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13114     else if (old_jx == jx && old_jy != jy)
13115       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13116
13117     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13118
13119     player->last_move_dir = player->MovDir;
13120     player->is_moving = TRUE;
13121     player->is_snapping = FALSE;
13122     player->is_switching = FALSE;
13123     player->is_dropping = FALSE;
13124     player->is_dropping_pressed = FALSE;
13125     player->drop_pressed_delay = 0;
13126
13127 #if 0
13128     // should better be called here than above, but this breaks some tapes
13129     ScrollPlayer(player, SCROLL_INIT);
13130 #endif
13131   }
13132   else
13133   {
13134     CheckGravityMovementWhenNotMoving(player);
13135
13136     player->is_moving = FALSE;
13137
13138     /* at this point, the player is allowed to move, but cannot move right now
13139        (e.g. because of something blocking the way) -- ensure that the player
13140        is also allowed to move in the next frame (in old versions before 3.1.1,
13141        the player was forced to wait again for eight frames before next try) */
13142
13143     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13144       player->move_delay = 0;   // allow direct movement in the next frame
13145   }
13146
13147   if (player->move_delay == -1)         // not yet initialized by DigField()
13148     player->move_delay = player->move_delay_value;
13149
13150   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13151   {
13152     TestIfPlayerTouchesBadThing(jx, jy);
13153     TestIfPlayerTouchesCustomElement(jx, jy);
13154   }
13155
13156   if (!player->active)
13157     RemovePlayer(player);
13158
13159   return moved;
13160 }
13161
13162 void ScrollPlayer(struct PlayerInfo *player, int mode)
13163 {
13164   int jx = player->jx, jy = player->jy;
13165   int last_jx = player->last_jx, last_jy = player->last_jy;
13166   int move_stepsize = TILEX / player->move_delay_value;
13167
13168   if (!player->active)
13169     return;
13170
13171   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13172     return;
13173
13174   if (mode == SCROLL_INIT)
13175   {
13176     player->actual_frame_counter.count = FrameCounter;
13177     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13178
13179     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13180         Tile[last_jx][last_jy] == EL_EMPTY)
13181     {
13182       int last_field_block_delay = 0;   // start with no blocking at all
13183       int block_delay_adjustment = player->block_delay_adjustment;
13184
13185       // if player blocks last field, add delay for exactly one move
13186       if (player->block_last_field)
13187       {
13188         last_field_block_delay += player->move_delay_value;
13189
13190         // when blocking enabled, prevent moving up despite gravity
13191         if (player->gravity && player->MovDir == MV_UP)
13192           block_delay_adjustment = -1;
13193       }
13194
13195       // add block delay adjustment (also possible when not blocking)
13196       last_field_block_delay += block_delay_adjustment;
13197
13198       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13199       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13200     }
13201
13202     if (player->MovPos != 0)    // player has not yet reached destination
13203       return;
13204   }
13205   else if (!FrameReached(&player->actual_frame_counter))
13206     return;
13207
13208   if (player->MovPos != 0)
13209   {
13210     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13211     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13212
13213     // before DrawPlayer() to draw correct player graphic for this case
13214     if (player->MovPos == 0)
13215       CheckGravityMovement(player);
13216   }
13217
13218   if (player->MovPos == 0)      // player reached destination field
13219   {
13220     if (player->move_delay_reset_counter > 0)
13221     {
13222       player->move_delay_reset_counter--;
13223
13224       if (player->move_delay_reset_counter == 0)
13225       {
13226         // continue with normal speed after quickly moving through gate
13227         HALVE_PLAYER_SPEED(player);
13228
13229         // be able to make the next move without delay
13230         player->move_delay = 0;
13231       }
13232     }
13233
13234     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13235         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13236         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13237         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13238         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13239         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13240         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13241         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13242     {
13243       ExitPlayer(player);
13244
13245       if (game.players_still_needed == 0 &&
13246           (game.friends_still_needed == 0 ||
13247            IS_SP_ELEMENT(Tile[jx][jy])))
13248         LevelSolved();
13249     }
13250
13251     player->last_jx = jx;
13252     player->last_jy = jy;
13253
13254     // this breaks one level: "machine", level 000
13255     {
13256       int move_direction = player->MovDir;
13257       int enter_side = MV_DIR_OPPOSITE(move_direction);
13258       int leave_side = move_direction;
13259       int old_jx = last_jx;
13260       int old_jy = last_jy;
13261       int old_element = Tile[old_jx][old_jy];
13262       int new_element = Tile[jx][jy];
13263
13264       if (IS_CUSTOM_ELEMENT(old_element))
13265         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13266                                    CE_LEFT_BY_PLAYER,
13267                                    player->index_bit, leave_side);
13268
13269       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13270                                           CE_PLAYER_LEAVES_X,
13271                                           player->index_bit, leave_side);
13272
13273       if (IS_CUSTOM_ELEMENT(new_element))
13274         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13275                                    player->index_bit, enter_side);
13276
13277       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13278                                           CE_PLAYER_ENTERS_X,
13279                                           player->index_bit, enter_side);
13280
13281       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13282                                         CE_MOVE_OF_X, move_direction);
13283     }
13284
13285     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13286     {
13287       TestIfPlayerTouchesBadThing(jx, jy);
13288       TestIfPlayerTouchesCustomElement(jx, jy);
13289
13290       /* needed because pushed element has not yet reached its destination,
13291          so it would trigger a change event at its previous field location */
13292       if (!player->is_pushing)
13293         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13294
13295       if (level.finish_dig_collect &&
13296           (player->is_digging || player->is_collecting))
13297       {
13298         int last_element = player->last_removed_element;
13299         int move_direction = player->MovDir;
13300         int enter_side = MV_DIR_OPPOSITE(move_direction);
13301         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13302                             CE_PLAYER_COLLECTS_X);
13303
13304         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13305                                             player->index_bit, enter_side);
13306
13307         player->last_removed_element = EL_UNDEFINED;
13308       }
13309
13310       if (!player->active)
13311         RemovePlayer(player);
13312     }
13313
13314     if (level.use_step_counter)
13315       CheckLevelTime_StepCounter();
13316
13317     if (tape.single_step && tape.recording && !tape.pausing &&
13318         !player->programmed_action)
13319       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13320
13321     if (!player->programmed_action)
13322       CheckSaveEngineSnapshot(player);
13323   }
13324 }
13325
13326 void ScrollScreen(struct PlayerInfo *player, int mode)
13327 {
13328   static DelayCounter screen_frame_counter = { 0 };
13329
13330   if (mode == SCROLL_INIT)
13331   {
13332     // set scrolling step size according to actual player's moving speed
13333     ScrollStepSize = TILEX / player->move_delay_value;
13334
13335     screen_frame_counter.count = FrameCounter;
13336     screen_frame_counter.value = 1;
13337
13338     ScreenMovDir = player->MovDir;
13339     ScreenMovPos = player->MovPos;
13340     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13341     return;
13342   }
13343   else if (!FrameReached(&screen_frame_counter))
13344     return;
13345
13346   if (ScreenMovPos)
13347   {
13348     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13349     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13350     redraw_mask |= REDRAW_FIELD;
13351   }
13352   else
13353     ScreenMovDir = MV_NONE;
13354 }
13355
13356 void CheckNextToConditions(int x, int y)
13357 {
13358   int element = Tile[x][y];
13359
13360   if (IS_PLAYER(x, y))
13361     TestIfPlayerNextToCustomElement(x, y);
13362
13363   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13364       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13365     TestIfElementNextToCustomElement(x, y);
13366 }
13367
13368 void TestIfPlayerNextToCustomElement(int x, int y)
13369 {
13370   struct XY *xy = xy_topdown;
13371   static int trigger_sides[4][2] =
13372   {
13373     // center side       border side
13374     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13375     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13376     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13377     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13378   };
13379   int i;
13380
13381   if (!IS_PLAYER(x, y))
13382     return;
13383
13384   struct PlayerInfo *player = PLAYERINFO(x, y);
13385
13386   if (player->is_moving)
13387     return;
13388
13389   for (i = 0; i < NUM_DIRECTIONS; i++)
13390   {
13391     int xx = x + xy[i].x;
13392     int yy = y + xy[i].y;
13393     int border_side = trigger_sides[i][1];
13394     int border_element;
13395
13396     if (!IN_LEV_FIELD(xx, yy))
13397       continue;
13398
13399     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13400       continue;         // center and border element not connected
13401
13402     border_element = Tile[xx][yy];
13403
13404     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13405                                player->index_bit, border_side);
13406     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13407                                         CE_PLAYER_NEXT_TO_X,
13408                                         player->index_bit, border_side);
13409
13410     /* use player element that is initially defined in the level playfield,
13411        not the player element that corresponds to the runtime player number
13412        (example: a level that contains EL_PLAYER_3 as the only player would
13413        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13414
13415     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13416                              CE_NEXT_TO_X, border_side);
13417   }
13418 }
13419
13420 void TestIfPlayerTouchesCustomElement(int x, int y)
13421 {
13422   struct XY *xy = xy_topdown;
13423   static int trigger_sides[4][2] =
13424   {
13425     // center side       border side
13426     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13427     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13428     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13429     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13430   };
13431   static int touch_dir[4] =
13432   {
13433     MV_LEFT | MV_RIGHT,
13434     MV_UP   | MV_DOWN,
13435     MV_UP   | MV_DOWN,
13436     MV_LEFT | MV_RIGHT
13437   };
13438   int center_element = Tile[x][y];      // should always be non-moving!
13439   int i;
13440
13441   for (i = 0; i < NUM_DIRECTIONS; i++)
13442   {
13443     int xx = x + xy[i].x;
13444     int yy = y + xy[i].y;
13445     int center_side = trigger_sides[i][0];
13446     int border_side = trigger_sides[i][1];
13447     int border_element;
13448
13449     if (!IN_LEV_FIELD(xx, yy))
13450       continue;
13451
13452     if (IS_PLAYER(x, y))                // player found at center element
13453     {
13454       struct PlayerInfo *player = PLAYERINFO(x, y);
13455
13456       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13457         border_element = Tile[xx][yy];          // may be moving!
13458       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13459         border_element = Tile[xx][yy];
13460       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13461         border_element = MovingOrBlocked2Element(xx, yy);
13462       else
13463         continue;               // center and border element do not touch
13464
13465       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13466                                  player->index_bit, border_side);
13467       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13468                                           CE_PLAYER_TOUCHES_X,
13469                                           player->index_bit, border_side);
13470
13471       {
13472         /* use player element that is initially defined in the level playfield,
13473            not the player element that corresponds to the runtime player number
13474            (example: a level that contains EL_PLAYER_3 as the only player would
13475            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13476         int player_element = PLAYERINFO(x, y)->initial_element;
13477
13478         CheckElementChangeBySide(xx, yy, border_element, player_element,
13479                                  CE_TOUCHING_X, border_side);
13480       }
13481     }
13482     else if (IS_PLAYER(xx, yy))         // player found at border element
13483     {
13484       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13485
13486       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13487       {
13488         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13489           continue;             // center and border element do not touch
13490       }
13491
13492       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13493                                  player->index_bit, center_side);
13494       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13495                                           CE_PLAYER_TOUCHES_X,
13496                                           player->index_bit, center_side);
13497
13498       {
13499         /* use player element that is initially defined in the level playfield,
13500            not the player element that corresponds to the runtime player number
13501            (example: a level that contains EL_PLAYER_3 as the only player would
13502            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13503         int player_element = PLAYERINFO(xx, yy)->initial_element;
13504
13505         CheckElementChangeBySide(x, y, center_element, player_element,
13506                                  CE_TOUCHING_X, center_side);
13507       }
13508
13509       break;
13510     }
13511   }
13512 }
13513
13514 void TestIfElementNextToCustomElement(int x, int y)
13515 {
13516   struct XY *xy = xy_topdown;
13517   static int trigger_sides[4][2] =
13518   {
13519     // center side      border side
13520     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13521     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13522     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13523     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13524   };
13525   int center_element = Tile[x][y];      // should always be non-moving!
13526   int i;
13527
13528   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13529     return;
13530
13531   for (i = 0; i < NUM_DIRECTIONS; i++)
13532   {
13533     int xx = x + xy[i].x;
13534     int yy = y + xy[i].y;
13535     int border_side = trigger_sides[i][1];
13536     int border_element;
13537
13538     if (!IN_LEV_FIELD(xx, yy))
13539       continue;
13540
13541     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13542       continue;                 // center and border element not connected
13543
13544     border_element = Tile[xx][yy];
13545
13546     // check for change of center element (but change it only once)
13547     if (CheckElementChangeBySide(x, y, center_element, border_element,
13548                                  CE_NEXT_TO_X, border_side))
13549       break;
13550   }
13551 }
13552
13553 void TestIfElementTouchesCustomElement(int x, int y)
13554 {
13555   struct XY *xy = xy_topdown;
13556   static int trigger_sides[4][2] =
13557   {
13558     // center side      border side
13559     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13560     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13561     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13562     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13563   };
13564   static int touch_dir[4] =
13565   {
13566     MV_LEFT | MV_RIGHT,
13567     MV_UP   | MV_DOWN,
13568     MV_UP   | MV_DOWN,
13569     MV_LEFT | MV_RIGHT
13570   };
13571   boolean change_center_element = FALSE;
13572   int center_element = Tile[x][y];      // should always be non-moving!
13573   int border_element_old[NUM_DIRECTIONS];
13574   int i;
13575
13576   for (i = 0; i < NUM_DIRECTIONS; i++)
13577   {
13578     int xx = x + xy[i].x;
13579     int yy = y + xy[i].y;
13580     int border_element;
13581
13582     border_element_old[i] = -1;
13583
13584     if (!IN_LEV_FIELD(xx, yy))
13585       continue;
13586
13587     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13588       border_element = Tile[xx][yy];    // may be moving!
13589     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13590       border_element = Tile[xx][yy];
13591     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13592       border_element = MovingOrBlocked2Element(xx, yy);
13593     else
13594       continue;                 // center and border element do not touch
13595
13596     border_element_old[i] = border_element;
13597   }
13598
13599   for (i = 0; i < NUM_DIRECTIONS; i++)
13600   {
13601     int xx = x + xy[i].x;
13602     int yy = y + xy[i].y;
13603     int center_side = trigger_sides[i][0];
13604     int border_element = border_element_old[i];
13605
13606     if (border_element == -1)
13607       continue;
13608
13609     // check for change of border element
13610     CheckElementChangeBySide(xx, yy, border_element, center_element,
13611                              CE_TOUCHING_X, center_side);
13612
13613     // (center element cannot be player, so we dont have to check this here)
13614   }
13615
13616   for (i = 0; i < NUM_DIRECTIONS; i++)
13617   {
13618     int xx = x + xy[i].x;
13619     int yy = y + xy[i].y;
13620     int border_side = trigger_sides[i][1];
13621     int border_element = border_element_old[i];
13622
13623     if (border_element == -1)
13624       continue;
13625
13626     // check for change of center element (but change it only once)
13627     if (!change_center_element)
13628       change_center_element =
13629         CheckElementChangeBySide(x, y, center_element, border_element,
13630                                  CE_TOUCHING_X, border_side);
13631
13632     if (IS_PLAYER(xx, yy))
13633     {
13634       /* use player element that is initially defined in the level playfield,
13635          not the player element that corresponds to the runtime player number
13636          (example: a level that contains EL_PLAYER_3 as the only player would
13637          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13638       int player_element = PLAYERINFO(xx, yy)->initial_element;
13639
13640       CheckElementChangeBySide(x, y, center_element, player_element,
13641                                CE_TOUCHING_X, border_side);
13642     }
13643   }
13644 }
13645
13646 void TestIfElementHitsCustomElement(int x, int y, int direction)
13647 {
13648   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13649   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13650   int hitx = x + dx, hity = y + dy;
13651   int hitting_element = Tile[x][y];
13652   int touched_element;
13653
13654   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13655     return;
13656
13657   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13658                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13659
13660   if (IN_LEV_FIELD(hitx, hity))
13661   {
13662     int opposite_direction = MV_DIR_OPPOSITE(direction);
13663     int hitting_side = direction;
13664     int touched_side = opposite_direction;
13665     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13666                           MovDir[hitx][hity] != direction ||
13667                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13668
13669     object_hit = TRUE;
13670
13671     if (object_hit)
13672     {
13673       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13674                                CE_HITTING_X, touched_side);
13675
13676       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13677                                CE_HIT_BY_X, hitting_side);
13678
13679       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13680                                CE_HIT_BY_SOMETHING, opposite_direction);
13681
13682       if (IS_PLAYER(hitx, hity))
13683       {
13684         /* use player element that is initially defined in the level playfield,
13685            not the player element that corresponds to the runtime player number
13686            (example: a level that contains EL_PLAYER_3 as the only player would
13687            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13688         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13689
13690         CheckElementChangeBySide(x, y, hitting_element, player_element,
13691                                  CE_HITTING_X, touched_side);
13692       }
13693     }
13694   }
13695
13696   // "hitting something" is also true when hitting the playfield border
13697   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13698                            CE_HITTING_SOMETHING, direction);
13699 }
13700
13701 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13702 {
13703   int i, kill_x = -1, kill_y = -1;
13704
13705   int bad_element = -1;
13706   struct XY *test_xy = xy_topdown;
13707   static int test_dir[4] =
13708   {
13709     MV_UP,
13710     MV_LEFT,
13711     MV_RIGHT,
13712     MV_DOWN
13713   };
13714
13715   for (i = 0; i < NUM_DIRECTIONS; i++)
13716   {
13717     int test_x, test_y, test_move_dir, test_element;
13718
13719     test_x = good_x + test_xy[i].x;
13720     test_y = good_y + test_xy[i].y;
13721
13722     if (!IN_LEV_FIELD(test_x, test_y))
13723       continue;
13724
13725     test_move_dir =
13726       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13727
13728     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13729
13730     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13731        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13732     */
13733     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13734         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13735     {
13736       kill_x = test_x;
13737       kill_y = test_y;
13738       bad_element = test_element;
13739
13740       break;
13741     }
13742   }
13743
13744   if (kill_x != -1 || kill_y != -1)
13745   {
13746     if (IS_PLAYER(good_x, good_y))
13747     {
13748       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13749
13750       if (player->shield_deadly_time_left > 0 &&
13751           !IS_INDESTRUCTIBLE(bad_element))
13752         Bang(kill_x, kill_y);
13753       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13754         KillPlayer(player);
13755     }
13756     else
13757       Bang(good_x, good_y);
13758   }
13759 }
13760
13761 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13762 {
13763   int i, kill_x = -1, kill_y = -1;
13764   int bad_element = Tile[bad_x][bad_y];
13765   struct XY *test_xy = xy_topdown;
13766   static int touch_dir[4] =
13767   {
13768     MV_LEFT | MV_RIGHT,
13769     MV_UP   | MV_DOWN,
13770     MV_UP   | MV_DOWN,
13771     MV_LEFT | MV_RIGHT
13772   };
13773   static int test_dir[4] =
13774   {
13775     MV_UP,
13776     MV_LEFT,
13777     MV_RIGHT,
13778     MV_DOWN
13779   };
13780
13781   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13782     return;
13783
13784   for (i = 0; i < NUM_DIRECTIONS; i++)
13785   {
13786     int test_x, test_y, test_move_dir, test_element;
13787
13788     test_x = bad_x + test_xy[i].x;
13789     test_y = bad_y + test_xy[i].y;
13790
13791     if (!IN_LEV_FIELD(test_x, test_y))
13792       continue;
13793
13794     test_move_dir =
13795       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13796
13797     test_element = Tile[test_x][test_y];
13798
13799     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13800        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13801     */
13802     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13803         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13804     {
13805       // good thing is player or penguin that does not move away
13806       if (IS_PLAYER(test_x, test_y))
13807       {
13808         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13809
13810         if (bad_element == EL_ROBOT && player->is_moving)
13811           continue;     // robot does not kill player if he is moving
13812
13813         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13814         {
13815           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13816             continue;           // center and border element do not touch
13817         }
13818
13819         kill_x = test_x;
13820         kill_y = test_y;
13821
13822         break;
13823       }
13824       else if (test_element == EL_PENGUIN)
13825       {
13826         kill_x = test_x;
13827         kill_y = test_y;
13828
13829         break;
13830       }
13831     }
13832   }
13833
13834   if (kill_x != -1 || kill_y != -1)
13835   {
13836     if (IS_PLAYER(kill_x, kill_y))
13837     {
13838       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13839
13840       if (player->shield_deadly_time_left > 0 &&
13841           !IS_INDESTRUCTIBLE(bad_element))
13842         Bang(bad_x, bad_y);
13843       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13844         KillPlayer(player);
13845     }
13846     else
13847       Bang(kill_x, kill_y);
13848   }
13849 }
13850
13851 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13852 {
13853   int bad_element = Tile[bad_x][bad_y];
13854   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13855   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13856   int test_x = bad_x + dx, test_y = bad_y + dy;
13857   int test_move_dir, test_element;
13858   int kill_x = -1, kill_y = -1;
13859
13860   if (!IN_LEV_FIELD(test_x, test_y))
13861     return;
13862
13863   test_move_dir =
13864     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13865
13866   test_element = Tile[test_x][test_y];
13867
13868   if (test_move_dir != bad_move_dir)
13869   {
13870     // good thing can be player or penguin that does not move away
13871     if (IS_PLAYER(test_x, test_y))
13872     {
13873       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13874
13875       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13876          player as being hit when he is moving towards the bad thing, because
13877          the "get hit by" condition would be lost after the player stops) */
13878       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13879         return;         // player moves away from bad thing
13880
13881       kill_x = test_x;
13882       kill_y = test_y;
13883     }
13884     else if (test_element == EL_PENGUIN)
13885     {
13886       kill_x = test_x;
13887       kill_y = test_y;
13888     }
13889   }
13890
13891   if (kill_x != -1 || kill_y != -1)
13892   {
13893     if (IS_PLAYER(kill_x, kill_y))
13894     {
13895       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13896
13897       if (player->shield_deadly_time_left > 0 &&
13898           !IS_INDESTRUCTIBLE(bad_element))
13899         Bang(bad_x, bad_y);
13900       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13901         KillPlayer(player);
13902     }
13903     else
13904       Bang(kill_x, kill_y);
13905   }
13906 }
13907
13908 void TestIfPlayerTouchesBadThing(int x, int y)
13909 {
13910   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13911 }
13912
13913 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13914 {
13915   TestIfGoodThingHitsBadThing(x, y, move_dir);
13916 }
13917
13918 void TestIfBadThingTouchesPlayer(int x, int y)
13919 {
13920   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13921 }
13922
13923 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13924 {
13925   TestIfBadThingHitsGoodThing(x, y, move_dir);
13926 }
13927
13928 void TestIfFriendTouchesBadThing(int x, int y)
13929 {
13930   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13931 }
13932
13933 void TestIfBadThingTouchesFriend(int x, int y)
13934 {
13935   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13936 }
13937
13938 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13939 {
13940   int i, kill_x = bad_x, kill_y = bad_y;
13941   struct XY *xy = xy_topdown;
13942
13943   for (i = 0; i < NUM_DIRECTIONS; i++)
13944   {
13945     int x, y, element;
13946
13947     x = bad_x + xy[i].x;
13948     y = bad_y + xy[i].y;
13949     if (!IN_LEV_FIELD(x, y))
13950       continue;
13951
13952     element = Tile[x][y];
13953     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13954         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13955     {
13956       kill_x = x;
13957       kill_y = y;
13958       break;
13959     }
13960   }
13961
13962   if (kill_x != bad_x || kill_y != bad_y)
13963     Bang(bad_x, bad_y);
13964 }
13965
13966 void KillPlayer(struct PlayerInfo *player)
13967 {
13968   int jx = player->jx, jy = player->jy;
13969
13970   if (!player->active)
13971     return;
13972
13973 #if 0
13974   Debug("game:playing:KillPlayer",
13975         "0: killed == %d, active == %d, reanimated == %d",
13976         player->killed, player->active, player->reanimated);
13977 #endif
13978
13979   /* the following code was introduced to prevent an infinite loop when calling
13980      -> Bang()
13981      -> CheckTriggeredElementChangeExt()
13982      -> ExecuteCustomElementAction()
13983      -> KillPlayer()
13984      -> (infinitely repeating the above sequence of function calls)
13985      which occurs when killing the player while having a CE with the setting
13986      "kill player X when explosion of <player X>"; the solution using a new
13987      field "player->killed" was chosen for backwards compatibility, although
13988      clever use of the fields "player->active" etc. would probably also work */
13989 #if 1
13990   if (player->killed)
13991     return;
13992 #endif
13993
13994   player->killed = TRUE;
13995
13996   // remove accessible field at the player's position
13997   Tile[jx][jy] = EL_EMPTY;
13998
13999   // deactivate shield (else Bang()/Explode() would not work right)
14000   player->shield_normal_time_left = 0;
14001   player->shield_deadly_time_left = 0;
14002
14003 #if 0
14004   Debug("game:playing:KillPlayer",
14005         "1: killed == %d, active == %d, reanimated == %d",
14006         player->killed, player->active, player->reanimated);
14007 #endif
14008
14009   Bang(jx, jy);
14010
14011 #if 0
14012   Debug("game:playing:KillPlayer",
14013         "2: killed == %d, active == %d, reanimated == %d",
14014         player->killed, player->active, player->reanimated);
14015 #endif
14016
14017   if (player->reanimated)       // killed player may have been reanimated
14018     player->killed = player->reanimated = FALSE;
14019   else
14020     BuryPlayer(player);
14021 }
14022
14023 static void KillPlayerUnlessEnemyProtected(int x, int y)
14024 {
14025   if (!PLAYER_ENEMY_PROTECTED(x, y))
14026     KillPlayer(PLAYERINFO(x, y));
14027 }
14028
14029 static void KillPlayerUnlessExplosionProtected(int x, int y)
14030 {
14031   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14032     KillPlayer(PLAYERINFO(x, y));
14033 }
14034
14035 void BuryPlayer(struct PlayerInfo *player)
14036 {
14037   int jx = player->jx, jy = player->jy;
14038
14039   if (!player->active)
14040     return;
14041
14042   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14043   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14044
14045   RemovePlayer(player);
14046
14047   player->buried = TRUE;
14048
14049   if (game.all_players_gone)
14050     game.GameOver = TRUE;
14051 }
14052
14053 void RemovePlayer(struct PlayerInfo *player)
14054 {
14055   int jx = player->jx, jy = player->jy;
14056   int i, found = FALSE;
14057
14058   player->present = FALSE;
14059   player->active = FALSE;
14060
14061   // required for some CE actions (even if the player is not active anymore)
14062   player->MovPos = 0;
14063
14064   if (!ExplodeField[jx][jy])
14065     StorePlayer[jx][jy] = 0;
14066
14067   if (player->is_moving)
14068     TEST_DrawLevelField(player->last_jx, player->last_jy);
14069
14070   for (i = 0; i < MAX_PLAYERS; i++)
14071     if (stored_player[i].active)
14072       found = TRUE;
14073
14074   if (!found)
14075   {
14076     game.all_players_gone = TRUE;
14077     game.GameOver = TRUE;
14078   }
14079
14080   game.exit_x = game.robot_wheel_x = jx;
14081   game.exit_y = game.robot_wheel_y = jy;
14082 }
14083
14084 void ExitPlayer(struct PlayerInfo *player)
14085 {
14086   DrawPlayer(player);   // needed here only to cleanup last field
14087   RemovePlayer(player);
14088
14089   if (game.players_still_needed > 0)
14090     game.players_still_needed--;
14091 }
14092
14093 static void SetFieldForSnapping(int x, int y, int element, int direction,
14094                                 int player_index_bit)
14095 {
14096   struct ElementInfo *ei = &element_info[element];
14097   int direction_bit = MV_DIR_TO_BIT(direction);
14098   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14099   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14100                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14101
14102   Tile[x][y] = EL_ELEMENT_SNAPPING;
14103   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14104   MovDir[x][y] = direction;
14105   Store[x][y] = element;
14106   Store2[x][y] = player_index_bit;
14107
14108   ResetGfxAnimation(x, y);
14109
14110   GfxElement[x][y] = element;
14111   GfxAction[x][y] = action;
14112   GfxDir[x][y] = direction;
14113   GfxFrame[x][y] = -1;
14114 }
14115
14116 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14117                                    int player_index_bit)
14118 {
14119   TestIfElementTouchesCustomElement(x, y);      // for empty space
14120
14121   if (level.finish_dig_collect)
14122   {
14123     int dig_side = MV_DIR_OPPOSITE(direction);
14124     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14125                         CE_PLAYER_COLLECTS_X);
14126
14127     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14128                                         player_index_bit, dig_side);
14129     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14130                                         player_index_bit, dig_side);
14131   }
14132 }
14133
14134 /*
14135   =============================================================================
14136   checkDiagonalPushing()
14137   -----------------------------------------------------------------------------
14138   check if diagonal input device direction results in pushing of object
14139   (by checking if the alternative direction is walkable, diggable, ...)
14140   =============================================================================
14141 */
14142
14143 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14144                                     int x, int y, int real_dx, int real_dy)
14145 {
14146   int jx, jy, dx, dy, xx, yy;
14147
14148   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14149     return TRUE;
14150
14151   // diagonal direction: check alternative direction
14152   jx = player->jx;
14153   jy = player->jy;
14154   dx = x - jx;
14155   dy = y - jy;
14156   xx = jx + (dx == 0 ? real_dx : 0);
14157   yy = jy + (dy == 0 ? real_dy : 0);
14158
14159   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14160 }
14161
14162 /*
14163   =============================================================================
14164   DigField()
14165   -----------------------------------------------------------------------------
14166   x, y:                 field next to player (non-diagonal) to try to dig to
14167   real_dx, real_dy:     direction as read from input device (can be diagonal)
14168   =============================================================================
14169 */
14170
14171 static int DigField(struct PlayerInfo *player,
14172                     int oldx, int oldy, int x, int y,
14173                     int real_dx, int real_dy, int mode)
14174 {
14175   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14176   boolean player_was_pushing = player->is_pushing;
14177   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14178   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14179   int jx = oldx, jy = oldy;
14180   int dx = x - jx, dy = y - jy;
14181   int nextx = x + dx, nexty = y + dy;
14182   int move_direction = (dx == -1 ? MV_LEFT  :
14183                         dx == +1 ? MV_RIGHT :
14184                         dy == -1 ? MV_UP    :
14185                         dy == +1 ? MV_DOWN  : MV_NONE);
14186   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14187   int dig_side = MV_DIR_OPPOSITE(move_direction);
14188   int old_element = Tile[jx][jy];
14189   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14190   int collect_count;
14191
14192   if (is_player)                // function can also be called by EL_PENGUIN
14193   {
14194     if (player->MovPos == 0)
14195     {
14196       player->is_digging = FALSE;
14197       player->is_collecting = FALSE;
14198     }
14199
14200     if (player->MovPos == 0)    // last pushing move finished
14201       player->is_pushing = FALSE;
14202
14203     if (mode == DF_NO_PUSH)     // player just stopped pushing
14204     {
14205       player->is_switching = FALSE;
14206       player->push_delay = -1;
14207
14208       return MP_NO_ACTION;
14209     }
14210   }
14211   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14212     old_element = Back[jx][jy];
14213
14214   // in case of element dropped at player position, check background
14215   else if (Back[jx][jy] != EL_EMPTY &&
14216            game.engine_version >= VERSION_IDENT(2,2,0,0))
14217     old_element = Back[jx][jy];
14218
14219   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14220     return MP_NO_ACTION;        // field has no opening in this direction
14221
14222   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14223     return MP_NO_ACTION;        // field has no opening in this direction
14224
14225   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14226   {
14227     SplashAcid(x, y);
14228
14229     Tile[jx][jy] = player->artwork_element;
14230     InitMovingField(jx, jy, MV_DOWN);
14231     Store[jx][jy] = EL_ACID;
14232     ContinueMoving(jx, jy);
14233     BuryPlayer(player);
14234
14235     return MP_DONT_RUN_INTO;
14236   }
14237
14238   if (player_can_move && DONT_RUN_INTO(element))
14239   {
14240     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14241
14242     return MP_DONT_RUN_INTO;
14243   }
14244
14245   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14246     return MP_NO_ACTION;
14247
14248   collect_count = element_info[element].collect_count_initial;
14249
14250   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14251     return MP_NO_ACTION;
14252
14253   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14254     player_can_move = player_can_move_or_snap;
14255
14256   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14257       game.engine_version >= VERSION_IDENT(2,2,0,0))
14258   {
14259     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14260                                player->index_bit, dig_side);
14261     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14262                                         player->index_bit, dig_side);
14263
14264     if (element == EL_DC_LANDMINE)
14265       Bang(x, y);
14266
14267     if (Tile[x][y] != element)          // field changed by snapping
14268       return MP_ACTION;
14269
14270     return MP_NO_ACTION;
14271   }
14272
14273   if (player->gravity && is_player && !player->is_auto_moving &&
14274       canFallDown(player) && move_direction != MV_DOWN &&
14275       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14276     return MP_NO_ACTION;        // player cannot walk here due to gravity
14277
14278   if (player_can_move &&
14279       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14280   {
14281     int sound_element = SND_ELEMENT(element);
14282     int sound_action = ACTION_WALKING;
14283
14284     if (IS_RND_GATE(element))
14285     {
14286       if (!player->key[RND_GATE_NR(element)])
14287         return MP_NO_ACTION;
14288     }
14289     else if (IS_RND_GATE_GRAY(element))
14290     {
14291       if (!player->key[RND_GATE_GRAY_NR(element)])
14292         return MP_NO_ACTION;
14293     }
14294     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14295     {
14296       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14297         return MP_NO_ACTION;
14298     }
14299     else if (element == EL_EXIT_OPEN ||
14300              element == EL_EM_EXIT_OPEN ||
14301              element == EL_EM_EXIT_OPENING ||
14302              element == EL_STEEL_EXIT_OPEN ||
14303              element == EL_EM_STEEL_EXIT_OPEN ||
14304              element == EL_EM_STEEL_EXIT_OPENING ||
14305              element == EL_SP_EXIT_OPEN ||
14306              element == EL_SP_EXIT_OPENING)
14307     {
14308       sound_action = ACTION_PASSING;    // player is passing exit
14309     }
14310     else if (element == EL_EMPTY)
14311     {
14312       sound_action = ACTION_MOVING;             // nothing to walk on
14313     }
14314
14315     // play sound from background or player, whatever is available
14316     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14317       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14318     else
14319       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14320   }
14321   else if (player_can_move &&
14322            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14323   {
14324     if (!ACCESS_FROM(element, opposite_direction))
14325       return MP_NO_ACTION;      // field not accessible from this direction
14326
14327     if (CAN_MOVE(element))      // only fixed elements can be passed!
14328       return MP_NO_ACTION;
14329
14330     if (IS_EM_GATE(element))
14331     {
14332       if (!player->key[EM_GATE_NR(element)])
14333         return MP_NO_ACTION;
14334     }
14335     else if (IS_EM_GATE_GRAY(element))
14336     {
14337       if (!player->key[EM_GATE_GRAY_NR(element)])
14338         return MP_NO_ACTION;
14339     }
14340     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14341     {
14342       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14343         return MP_NO_ACTION;
14344     }
14345     else if (IS_EMC_GATE(element))
14346     {
14347       if (!player->key[EMC_GATE_NR(element)])
14348         return MP_NO_ACTION;
14349     }
14350     else if (IS_EMC_GATE_GRAY(element))
14351     {
14352       if (!player->key[EMC_GATE_GRAY_NR(element)])
14353         return MP_NO_ACTION;
14354     }
14355     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14356     {
14357       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14358         return MP_NO_ACTION;
14359     }
14360     else if (element == EL_DC_GATE_WHITE ||
14361              element == EL_DC_GATE_WHITE_GRAY ||
14362              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14363     {
14364       if (player->num_white_keys == 0)
14365         return MP_NO_ACTION;
14366
14367       player->num_white_keys--;
14368     }
14369     else if (IS_SP_PORT(element))
14370     {
14371       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14372           element == EL_SP_GRAVITY_PORT_RIGHT ||
14373           element == EL_SP_GRAVITY_PORT_UP ||
14374           element == EL_SP_GRAVITY_PORT_DOWN)
14375         player->gravity = !player->gravity;
14376       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14377                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14378                element == EL_SP_GRAVITY_ON_PORT_UP ||
14379                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14380         player->gravity = TRUE;
14381       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14382                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14383                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14384                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14385         player->gravity = FALSE;
14386     }
14387
14388     // automatically move to the next field with double speed
14389     player->programmed_action = move_direction;
14390
14391     if (player->move_delay_reset_counter == 0)
14392     {
14393       player->move_delay_reset_counter = 2;     // two double speed steps
14394
14395       DOUBLE_PLAYER_SPEED(player);
14396     }
14397
14398     PlayLevelSoundAction(x, y, ACTION_PASSING);
14399   }
14400   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14401   {
14402     RemoveField(x, y);
14403
14404     if (mode != DF_SNAP)
14405     {
14406       GfxElement[x][y] = GFX_ELEMENT(element);
14407       player->is_digging = TRUE;
14408     }
14409
14410     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14411
14412     // use old behaviour for old levels (digging)
14413     if (!level.finish_dig_collect)
14414     {
14415       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14416                                           player->index_bit, dig_side);
14417
14418       // if digging triggered player relocation, finish digging tile
14419       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14420         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14421     }
14422
14423     if (mode == DF_SNAP)
14424     {
14425       if (level.block_snap_field)
14426         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14427       else
14428         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14429
14430       // use old behaviour for old levels (snapping)
14431       if (!level.finish_dig_collect)
14432         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14433                                             player->index_bit, dig_side);
14434     }
14435   }
14436   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14437   {
14438     RemoveField(x, y);
14439
14440     if (is_player && mode != DF_SNAP)
14441     {
14442       GfxElement[x][y] = element;
14443       player->is_collecting = TRUE;
14444     }
14445
14446     if (element == EL_SPEED_PILL)
14447     {
14448       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14449     }
14450     else if (element == EL_EXTRA_TIME && level.time > 0)
14451     {
14452       TimeLeft += level.extra_time;
14453
14454       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14455
14456       DisplayGameControlValues();
14457     }
14458     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14459     {
14460       int shield_time = (element == EL_SHIELD_DEADLY ?
14461                          level.shield_deadly_time :
14462                          level.shield_normal_time);
14463
14464       player->shield_normal_time_left += shield_time;
14465       if (element == EL_SHIELD_DEADLY)
14466         player->shield_deadly_time_left += shield_time;
14467     }
14468     else if (element == EL_DYNAMITE ||
14469              element == EL_EM_DYNAMITE ||
14470              element == EL_SP_DISK_RED)
14471     {
14472       if (player->inventory_size < MAX_INVENTORY_SIZE)
14473         player->inventory_element[player->inventory_size++] = element;
14474
14475       DrawGameDoorValues();
14476     }
14477     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14478     {
14479       player->dynabomb_count++;
14480       player->dynabombs_left++;
14481     }
14482     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14483     {
14484       player->dynabomb_size++;
14485     }
14486     else if (element == EL_DYNABOMB_INCREASE_POWER)
14487     {
14488       player->dynabomb_xl = TRUE;
14489     }
14490     else if (IS_KEY(element))
14491     {
14492       player->key[KEY_NR(element)] = TRUE;
14493
14494       DrawGameDoorValues();
14495     }
14496     else if (element == EL_DC_KEY_WHITE)
14497     {
14498       player->num_white_keys++;
14499
14500       // display white keys?
14501       // DrawGameDoorValues();
14502     }
14503     else if (IS_ENVELOPE(element))
14504     {
14505       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14506
14507       if (!wait_for_snapping)
14508         player->show_envelope = element;
14509     }
14510     else if (element == EL_EMC_LENSES)
14511     {
14512       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14513
14514       RedrawAllInvisibleElementsForLenses();
14515     }
14516     else if (element == EL_EMC_MAGNIFIER)
14517     {
14518       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14519
14520       RedrawAllInvisibleElementsForMagnifier();
14521     }
14522     else if (IS_DROPPABLE(element) ||
14523              IS_THROWABLE(element))     // can be collected and dropped
14524     {
14525       int i;
14526
14527       if (collect_count == 0)
14528         player->inventory_infinite_element = element;
14529       else
14530         for (i = 0; i < collect_count; i++)
14531           if (player->inventory_size < MAX_INVENTORY_SIZE)
14532             player->inventory_element[player->inventory_size++] = element;
14533
14534       DrawGameDoorValues();
14535     }
14536     else if (collect_count > 0)
14537     {
14538       game.gems_still_needed -= collect_count;
14539       if (game.gems_still_needed < 0)
14540         game.gems_still_needed = 0;
14541
14542       game.snapshot.collected_item = TRUE;
14543
14544       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14545
14546       DisplayGameControlValues();
14547     }
14548
14549     RaiseScoreElement(element);
14550     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14551
14552     // use old behaviour for old levels (collecting)
14553     if (!level.finish_dig_collect && is_player)
14554     {
14555       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14556                                           player->index_bit, dig_side);
14557
14558       // if collecting triggered player relocation, finish collecting tile
14559       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14560         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14561     }
14562
14563     if (mode == DF_SNAP)
14564     {
14565       if (level.block_snap_field)
14566         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14567       else
14568         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14569
14570       // use old behaviour for old levels (snapping)
14571       if (!level.finish_dig_collect)
14572         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14573                                             player->index_bit, dig_side);
14574     }
14575   }
14576   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14577   {
14578     if (mode == DF_SNAP && element != EL_BD_ROCK)
14579       return MP_NO_ACTION;
14580
14581     if (CAN_FALL(element) && dy)
14582       return MP_NO_ACTION;
14583
14584     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14585         !(element == EL_SPRING && level.use_spring_bug))
14586       return MP_NO_ACTION;
14587
14588     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14589         ((move_direction & MV_VERTICAL &&
14590           ((element_info[element].move_pattern & MV_LEFT &&
14591             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14592            (element_info[element].move_pattern & MV_RIGHT &&
14593             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14594          (move_direction & MV_HORIZONTAL &&
14595           ((element_info[element].move_pattern & MV_UP &&
14596             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14597            (element_info[element].move_pattern & MV_DOWN &&
14598             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14599       return MP_NO_ACTION;
14600
14601     // do not push elements already moving away faster than player
14602     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14603         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14604       return MP_NO_ACTION;
14605
14606     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14607     {
14608       if (player->push_delay_value == -1 || !player_was_pushing)
14609         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14610     }
14611     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14612     {
14613       if (player->push_delay_value == -1)
14614         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14615     }
14616     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14617     {
14618       if (!player->is_pushing)
14619         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14620     }
14621
14622     player->is_pushing = TRUE;
14623     player->is_active = TRUE;
14624
14625     if (!(IN_LEV_FIELD(nextx, nexty) &&
14626           (IS_FREE(nextx, nexty) ||
14627            (IS_SB_ELEMENT(element) &&
14628             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14629            (IS_CUSTOM_ELEMENT(element) &&
14630             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14631       return MP_NO_ACTION;
14632
14633     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14634       return MP_NO_ACTION;
14635
14636     if (player->push_delay == -1)       // new pushing; restart delay
14637       player->push_delay = 0;
14638
14639     if (player->push_delay < player->push_delay_value &&
14640         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14641         element != EL_SPRING && element != EL_BALLOON)
14642     {
14643       // make sure that there is no move delay before next try to push
14644       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14645         player->move_delay = 0;
14646
14647       return MP_NO_ACTION;
14648     }
14649
14650     if (IS_CUSTOM_ELEMENT(element) &&
14651         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14652     {
14653       if (!DigFieldByCE(nextx, nexty, element))
14654         return MP_NO_ACTION;
14655     }
14656
14657     if (IS_SB_ELEMENT(element))
14658     {
14659       boolean sokoban_task_solved = FALSE;
14660
14661       if (element == EL_SOKOBAN_FIELD_FULL)
14662       {
14663         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14664
14665         IncrementSokobanFieldsNeeded();
14666         IncrementSokobanObjectsNeeded();
14667       }
14668
14669       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14670       {
14671         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14672
14673         DecrementSokobanFieldsNeeded();
14674         DecrementSokobanObjectsNeeded();
14675
14676         // sokoban object was pushed from empty field to sokoban field
14677         if (Back[x][y] == EL_EMPTY)
14678           sokoban_task_solved = TRUE;
14679       }
14680
14681       Tile[x][y] = EL_SOKOBAN_OBJECT;
14682
14683       if (Back[x][y] == Back[nextx][nexty])
14684         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14685       else if (Back[x][y] != 0)
14686         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14687                                     ACTION_EMPTYING);
14688       else
14689         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14690                                     ACTION_FILLING);
14691
14692       if (sokoban_task_solved &&
14693           game.sokoban_fields_still_needed == 0 &&
14694           game.sokoban_objects_still_needed == 0 &&
14695           level.auto_exit_sokoban)
14696       {
14697         game.players_still_needed = 0;
14698
14699         LevelSolved();
14700
14701         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14702       }
14703     }
14704     else
14705       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14706
14707     InitMovingField(x, y, move_direction);
14708     GfxAction[x][y] = ACTION_PUSHING;
14709
14710     if (mode == DF_SNAP)
14711       ContinueMoving(x, y);
14712     else
14713       MovPos[x][y] = (dx != 0 ? dx : dy);
14714
14715     Pushed[x][y] = TRUE;
14716     Pushed[nextx][nexty] = TRUE;
14717
14718     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14719       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14720     else
14721       player->push_delay_value = -1;    // get new value later
14722
14723     // check for element change _after_ element has been pushed
14724     if (game.use_change_when_pushing_bug)
14725     {
14726       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14727                                  player->index_bit, dig_side);
14728       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14729                                           player->index_bit, dig_side);
14730     }
14731   }
14732   else if (IS_SWITCHABLE(element))
14733   {
14734     if (PLAYER_SWITCHING(player, x, y))
14735     {
14736       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14737                                           player->index_bit, dig_side);
14738
14739       return MP_ACTION;
14740     }
14741
14742     player->is_switching = TRUE;
14743     player->switch_x = x;
14744     player->switch_y = y;
14745
14746     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14747
14748     if (element == EL_ROBOT_WHEEL)
14749     {
14750       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14751
14752       game.robot_wheel_x = x;
14753       game.robot_wheel_y = y;
14754       game.robot_wheel_active = TRUE;
14755
14756       TEST_DrawLevelField(x, y);
14757     }
14758     else if (element == EL_SP_TERMINAL)
14759     {
14760       int xx, yy;
14761
14762       SCAN_PLAYFIELD(xx, yy)
14763       {
14764         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14765         {
14766           Bang(xx, yy);
14767         }
14768         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14769         {
14770           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14771
14772           ResetGfxAnimation(xx, yy);
14773           TEST_DrawLevelField(xx, yy);
14774         }
14775       }
14776     }
14777     else if (IS_BELT_SWITCH(element))
14778     {
14779       ToggleBeltSwitch(x, y);
14780     }
14781     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14782              element == EL_SWITCHGATE_SWITCH_DOWN ||
14783              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14784              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14785     {
14786       ToggleSwitchgateSwitch(x, y);
14787     }
14788     else if (element == EL_LIGHT_SWITCH ||
14789              element == EL_LIGHT_SWITCH_ACTIVE)
14790     {
14791       ToggleLightSwitch(x, y);
14792     }
14793     else if (element == EL_TIMEGATE_SWITCH ||
14794              element == EL_DC_TIMEGATE_SWITCH)
14795     {
14796       ActivateTimegateSwitch(x, y);
14797     }
14798     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14799              element == EL_BALLOON_SWITCH_RIGHT ||
14800              element == EL_BALLOON_SWITCH_UP    ||
14801              element == EL_BALLOON_SWITCH_DOWN  ||
14802              element == EL_BALLOON_SWITCH_NONE  ||
14803              element == EL_BALLOON_SWITCH_ANY)
14804     {
14805       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14806                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14807                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14808                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14809                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14810                              move_direction);
14811     }
14812     else if (element == EL_LAMP)
14813     {
14814       Tile[x][y] = EL_LAMP_ACTIVE;
14815       game.lights_still_needed--;
14816
14817       ResetGfxAnimation(x, y);
14818       TEST_DrawLevelField(x, y);
14819     }
14820     else if (element == EL_TIME_ORB_FULL)
14821     {
14822       Tile[x][y] = EL_TIME_ORB_EMPTY;
14823
14824       if (level.time > 0 || level.use_time_orb_bug)
14825       {
14826         TimeLeft += level.time_orb_time;
14827         game.no_level_time_limit = FALSE;
14828
14829         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14830
14831         DisplayGameControlValues();
14832       }
14833
14834       ResetGfxAnimation(x, y);
14835       TEST_DrawLevelField(x, y);
14836     }
14837     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14838              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14839     {
14840       int xx, yy;
14841
14842       game.ball_active = !game.ball_active;
14843
14844       SCAN_PLAYFIELD(xx, yy)
14845       {
14846         int e = Tile[xx][yy];
14847
14848         if (game.ball_active)
14849         {
14850           if (e == EL_EMC_MAGIC_BALL)
14851             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14852           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14853             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14854         }
14855         else
14856         {
14857           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14858             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14859           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14860             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14861         }
14862       }
14863     }
14864
14865     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14866                                         player->index_bit, dig_side);
14867
14868     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14869                                         player->index_bit, dig_side);
14870
14871     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14872                                         player->index_bit, dig_side);
14873
14874     return MP_ACTION;
14875   }
14876   else
14877   {
14878     if (!PLAYER_SWITCHING(player, x, y))
14879     {
14880       player->is_switching = TRUE;
14881       player->switch_x = x;
14882       player->switch_y = y;
14883
14884       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14885                                  player->index_bit, dig_side);
14886       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14887                                           player->index_bit, dig_side);
14888
14889       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14890                                  player->index_bit, dig_side);
14891       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14892                                           player->index_bit, dig_side);
14893     }
14894
14895     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14896                                player->index_bit, dig_side);
14897     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14898                                         player->index_bit, dig_side);
14899
14900     return MP_NO_ACTION;
14901   }
14902
14903   player->push_delay = -1;
14904
14905   if (is_player)                // function can also be called by EL_PENGUIN
14906   {
14907     if (Tile[x][y] != element)          // really digged/collected something
14908     {
14909       player->is_collecting = !player->is_digging;
14910       player->is_active = TRUE;
14911
14912       player->last_removed_element = element;
14913     }
14914   }
14915
14916   return MP_MOVING;
14917 }
14918
14919 static boolean DigFieldByCE(int x, int y, int digging_element)
14920 {
14921   int element = Tile[x][y];
14922
14923   if (!IS_FREE(x, y))
14924   {
14925     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14926                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14927                   ACTION_BREAKING);
14928
14929     // no element can dig solid indestructible elements
14930     if (IS_INDESTRUCTIBLE(element) &&
14931         !IS_DIGGABLE(element) &&
14932         !IS_COLLECTIBLE(element))
14933       return FALSE;
14934
14935     if (AmoebaNr[x][y] &&
14936         (element == EL_AMOEBA_FULL ||
14937          element == EL_BD_AMOEBA ||
14938          element == EL_AMOEBA_GROWING))
14939     {
14940       AmoebaCnt[AmoebaNr[x][y]]--;
14941       AmoebaCnt2[AmoebaNr[x][y]]--;
14942     }
14943
14944     if (IS_MOVING(x, y))
14945       RemoveMovingField(x, y);
14946     else
14947     {
14948       RemoveField(x, y);
14949       TEST_DrawLevelField(x, y);
14950     }
14951
14952     // if digged element was about to explode, prevent the explosion
14953     ExplodeField[x][y] = EX_TYPE_NONE;
14954
14955     PlayLevelSoundAction(x, y, action);
14956   }
14957
14958   Store[x][y] = EL_EMPTY;
14959
14960   // this makes it possible to leave the removed element again
14961   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14962     Store[x][y] = element;
14963
14964   return TRUE;
14965 }
14966
14967 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14968 {
14969   int jx = player->jx, jy = player->jy;
14970   int x = jx + dx, y = jy + dy;
14971   int snap_direction = (dx == -1 ? MV_LEFT  :
14972                         dx == +1 ? MV_RIGHT :
14973                         dy == -1 ? MV_UP    :
14974                         dy == +1 ? MV_DOWN  : MV_NONE);
14975   boolean can_continue_snapping = (level.continuous_snapping &&
14976                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14977
14978   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14979     return FALSE;
14980
14981   if (!player->active || !IN_LEV_FIELD(x, y))
14982     return FALSE;
14983
14984   if (dx && dy)
14985     return FALSE;
14986
14987   if (!dx && !dy)
14988   {
14989     if (player->MovPos == 0)
14990       player->is_pushing = FALSE;
14991
14992     player->is_snapping = FALSE;
14993
14994     if (player->MovPos == 0)
14995     {
14996       player->is_moving = FALSE;
14997       player->is_digging = FALSE;
14998       player->is_collecting = FALSE;
14999     }
15000
15001     return FALSE;
15002   }
15003
15004   // prevent snapping with already pressed snap key when not allowed
15005   if (player->is_snapping && !can_continue_snapping)
15006     return FALSE;
15007
15008   player->MovDir = snap_direction;
15009
15010   if (player->MovPos == 0)
15011   {
15012     player->is_moving = FALSE;
15013     player->is_digging = FALSE;
15014     player->is_collecting = FALSE;
15015   }
15016
15017   player->is_dropping = FALSE;
15018   player->is_dropping_pressed = FALSE;
15019   player->drop_pressed_delay = 0;
15020
15021   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15022     return FALSE;
15023
15024   player->is_snapping = TRUE;
15025   player->is_active = TRUE;
15026
15027   if (player->MovPos == 0)
15028   {
15029     player->is_moving = FALSE;
15030     player->is_digging = FALSE;
15031     player->is_collecting = FALSE;
15032   }
15033
15034   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15035     TEST_DrawLevelField(player->last_jx, player->last_jy);
15036
15037   TEST_DrawLevelField(x, y);
15038
15039   return TRUE;
15040 }
15041
15042 static boolean DropElement(struct PlayerInfo *player)
15043 {
15044   int old_element, new_element;
15045   int dropx = player->jx, dropy = player->jy;
15046   int drop_direction = player->MovDir;
15047   int drop_side = drop_direction;
15048   int drop_element = get_next_dropped_element(player);
15049
15050   /* do not drop an element on top of another element; when holding drop key
15051      pressed without moving, dropped element must move away before the next
15052      element can be dropped (this is especially important if the next element
15053      is dynamite, which can be placed on background for historical reasons) */
15054   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15055     return MP_ACTION;
15056
15057   if (IS_THROWABLE(drop_element))
15058   {
15059     dropx += GET_DX_FROM_DIR(drop_direction);
15060     dropy += GET_DY_FROM_DIR(drop_direction);
15061
15062     if (!IN_LEV_FIELD(dropx, dropy))
15063       return FALSE;
15064   }
15065
15066   old_element = Tile[dropx][dropy];     // old element at dropping position
15067   new_element = drop_element;           // default: no change when dropping
15068
15069   // check if player is active, not moving and ready to drop
15070   if (!player->active || player->MovPos || player->drop_delay > 0)
15071     return FALSE;
15072
15073   // check if player has anything that can be dropped
15074   if (new_element == EL_UNDEFINED)
15075     return FALSE;
15076
15077   // only set if player has anything that can be dropped
15078   player->is_dropping_pressed = TRUE;
15079
15080   // check if drop key was pressed long enough for EM style dynamite
15081   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15082     return FALSE;
15083
15084   // check if anything can be dropped at the current position
15085   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15086     return FALSE;
15087
15088   // collected custom elements can only be dropped on empty fields
15089   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15090     return FALSE;
15091
15092   if (old_element != EL_EMPTY)
15093     Back[dropx][dropy] = old_element;   // store old element on this field
15094
15095   ResetGfxAnimation(dropx, dropy);
15096   ResetRandomAnimationValue(dropx, dropy);
15097
15098   if (player->inventory_size > 0 ||
15099       player->inventory_infinite_element != EL_UNDEFINED)
15100   {
15101     if (player->inventory_size > 0)
15102     {
15103       player->inventory_size--;
15104
15105       DrawGameDoorValues();
15106
15107       if (new_element == EL_DYNAMITE)
15108         new_element = EL_DYNAMITE_ACTIVE;
15109       else if (new_element == EL_EM_DYNAMITE)
15110         new_element = EL_EM_DYNAMITE_ACTIVE;
15111       else if (new_element == EL_SP_DISK_RED)
15112         new_element = EL_SP_DISK_RED_ACTIVE;
15113     }
15114
15115     Tile[dropx][dropy] = new_element;
15116
15117     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15118       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15119                           el2img(Tile[dropx][dropy]), 0);
15120
15121     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15122
15123     // needed if previous element just changed to "empty" in the last frame
15124     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15125
15126     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15127                                player->index_bit, drop_side);
15128     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15129                                         CE_PLAYER_DROPS_X,
15130                                         player->index_bit, drop_side);
15131
15132     TestIfElementTouchesCustomElement(dropx, dropy);
15133   }
15134   else          // player is dropping a dyna bomb
15135   {
15136     player->dynabombs_left--;
15137
15138     Tile[dropx][dropy] = new_element;
15139
15140     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15141       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15142                           el2img(Tile[dropx][dropy]), 0);
15143
15144     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15145   }
15146
15147   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15148     InitField_WithBug1(dropx, dropy, FALSE);
15149
15150   new_element = Tile[dropx][dropy];     // element might have changed
15151
15152   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15153       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15154   {
15155     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15156       MovDir[dropx][dropy] = drop_direction;
15157
15158     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15159
15160     // do not cause impact style collision by dropping elements that can fall
15161     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15162   }
15163
15164   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15165   player->is_dropping = TRUE;
15166
15167   player->drop_pressed_delay = 0;
15168   player->is_dropping_pressed = FALSE;
15169
15170   player->drop_x = dropx;
15171   player->drop_y = dropy;
15172
15173   return TRUE;
15174 }
15175
15176 // ----------------------------------------------------------------------------
15177 // game sound playing functions
15178 // ----------------------------------------------------------------------------
15179
15180 static int *loop_sound_frame = NULL;
15181 static int *loop_sound_volume = NULL;
15182
15183 void InitPlayLevelSound(void)
15184 {
15185   int num_sounds = getSoundListSize();
15186
15187   checked_free(loop_sound_frame);
15188   checked_free(loop_sound_volume);
15189
15190   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15191   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15192 }
15193
15194 static void PlayLevelSound(int x, int y, int nr)
15195 {
15196   int sx = SCREENX(x), sy = SCREENY(y);
15197   int volume, stereo_position;
15198   int max_distance = 8;
15199   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15200
15201   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15202       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15203     return;
15204
15205   if (!IN_LEV_FIELD(x, y) ||
15206       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15207       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15208     return;
15209
15210   volume = SOUND_MAX_VOLUME;
15211
15212   if (!IN_SCR_FIELD(sx, sy))
15213   {
15214     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15215     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15216
15217     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15218   }
15219
15220   stereo_position = (SOUND_MAX_LEFT +
15221                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15222                      (SCR_FIELDX + 2 * max_distance));
15223
15224   if (IS_LOOP_SOUND(nr))
15225   {
15226     /* This assures that quieter loop sounds do not overwrite louder ones,
15227        while restarting sound volume comparison with each new game frame. */
15228
15229     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15230       return;
15231
15232     loop_sound_volume[nr] = volume;
15233     loop_sound_frame[nr] = FrameCounter;
15234   }
15235
15236   PlaySoundExt(nr, volume, stereo_position, type);
15237 }
15238
15239 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15240 {
15241   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15242                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15243                  y < LEVELY(BY1) ? LEVELY(BY1) :
15244                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15245                  sound_action);
15246 }
15247
15248 static void PlayLevelSoundAction(int x, int y, int action)
15249 {
15250   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15251 }
15252
15253 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15254 {
15255   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15256
15257   if (sound_effect != SND_UNDEFINED)
15258     PlayLevelSound(x, y, sound_effect);
15259 }
15260
15261 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15262                                               int action)
15263 {
15264   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15265
15266   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15267     PlayLevelSound(x, y, sound_effect);
15268 }
15269
15270 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15271 {
15272   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15273
15274   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15275     PlayLevelSound(x, y, sound_effect);
15276 }
15277
15278 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15279 {
15280   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15281
15282   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15283     StopSound(sound_effect);
15284 }
15285
15286 static int getLevelMusicNr(void)
15287 {
15288   if (levelset.music[level_nr] != MUS_UNDEFINED)
15289     return levelset.music[level_nr];            // from config file
15290   else
15291     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15292 }
15293
15294 static void FadeLevelSounds(void)
15295 {
15296   FadeSounds();
15297 }
15298
15299 static void FadeLevelMusic(void)
15300 {
15301   int music_nr = getLevelMusicNr();
15302   char *curr_music = getCurrentlyPlayingMusicFilename();
15303   char *next_music = getMusicInfoEntryFilename(music_nr);
15304
15305   if (!strEqual(curr_music, next_music))
15306     FadeMusic();
15307 }
15308
15309 void FadeLevelSoundsAndMusic(void)
15310 {
15311   FadeLevelSounds();
15312   FadeLevelMusic();
15313 }
15314
15315 static void PlayLevelMusic(void)
15316 {
15317   int music_nr = getLevelMusicNr();
15318   char *curr_music = getCurrentlyPlayingMusicFilename();
15319   char *next_music = getMusicInfoEntryFilename(music_nr);
15320
15321   if (!strEqual(curr_music, next_music))
15322     PlayMusicLoop(music_nr);
15323 }
15324
15325 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15326 {
15327   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15328   int offset = 0;
15329   int x = xx - offset;
15330   int y = yy - offset;
15331
15332   switch (sample)
15333   {
15334     case SOUND_blank:
15335       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15336       break;
15337
15338     case SOUND_roll:
15339       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15340       break;
15341
15342     case SOUND_stone:
15343       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15344       break;
15345
15346     case SOUND_nut:
15347       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15348       break;
15349
15350     case SOUND_crack:
15351       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15352       break;
15353
15354     case SOUND_bug:
15355       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15356       break;
15357
15358     case SOUND_tank:
15359       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15360       break;
15361
15362     case SOUND_android_clone:
15363       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15364       break;
15365
15366     case SOUND_android_move:
15367       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15368       break;
15369
15370     case SOUND_spring:
15371       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15372       break;
15373
15374     case SOUND_slurp:
15375       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15376       break;
15377
15378     case SOUND_eater:
15379       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15380       break;
15381
15382     case SOUND_eater_eat:
15383       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15384       break;
15385
15386     case SOUND_alien:
15387       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15388       break;
15389
15390     case SOUND_collect:
15391       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15392       break;
15393
15394     case SOUND_diamond:
15395       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15396       break;
15397
15398     case SOUND_squash:
15399       // !!! CHECK THIS !!!
15400 #if 1
15401       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15402 #else
15403       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15404 #endif
15405       break;
15406
15407     case SOUND_wonderfall:
15408       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15409       break;
15410
15411     case SOUND_drip:
15412       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15413       break;
15414
15415     case SOUND_push:
15416       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15417       break;
15418
15419     case SOUND_dirt:
15420       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15421       break;
15422
15423     case SOUND_acid:
15424       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15425       break;
15426
15427     case SOUND_ball:
15428       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15429       break;
15430
15431     case SOUND_slide:
15432       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15433       break;
15434
15435     case SOUND_wonder:
15436       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15437       break;
15438
15439     case SOUND_door:
15440       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15441       break;
15442
15443     case SOUND_exit_open:
15444       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15445       break;
15446
15447     case SOUND_exit_leave:
15448       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15449       break;
15450
15451     case SOUND_dynamite:
15452       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15453       break;
15454
15455     case SOUND_tick:
15456       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15457       break;
15458
15459     case SOUND_press:
15460       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15461       break;
15462
15463     case SOUND_wheel:
15464       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15465       break;
15466
15467     case SOUND_boom:
15468       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15469       break;
15470
15471     case SOUND_die:
15472       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15473       break;
15474
15475     case SOUND_time:
15476       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15477       break;
15478
15479     default:
15480       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15481       break;
15482   }
15483 }
15484
15485 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15486 {
15487   int element = map_element_SP_to_RND(element_sp);
15488   int action = map_action_SP_to_RND(action_sp);
15489   int offset = (setup.sp_show_border_elements ? 0 : 1);
15490   int x = xx - offset;
15491   int y = yy - offset;
15492
15493   PlayLevelSoundElementAction(x, y, element, action);
15494 }
15495
15496 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15497 {
15498   int element = map_element_MM_to_RND(element_mm);
15499   int action = map_action_MM_to_RND(action_mm);
15500   int offset = 0;
15501   int x = xx - offset;
15502   int y = yy - offset;
15503
15504   if (!IS_MM_ELEMENT(element))
15505     element = EL_MM_DEFAULT;
15506
15507   PlayLevelSoundElementAction(x, y, element, action);
15508 }
15509
15510 void PlaySound_MM(int sound_mm)
15511 {
15512   int sound = map_sound_MM_to_RND(sound_mm);
15513
15514   if (sound == SND_UNDEFINED)
15515     return;
15516
15517   PlaySound(sound);
15518 }
15519
15520 void PlaySoundLoop_MM(int sound_mm)
15521 {
15522   int sound = map_sound_MM_to_RND(sound_mm);
15523
15524   if (sound == SND_UNDEFINED)
15525     return;
15526
15527   PlaySoundLoop(sound);
15528 }
15529
15530 void StopSound_MM(int sound_mm)
15531 {
15532   int sound = map_sound_MM_to_RND(sound_mm);
15533
15534   if (sound == SND_UNDEFINED)
15535     return;
15536
15537   StopSound(sound);
15538 }
15539
15540 void RaiseScore(int value)
15541 {
15542   game.score += value;
15543
15544   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15545
15546   DisplayGameControlValues();
15547 }
15548
15549 void RaiseScoreElement(int element)
15550 {
15551   switch (element)
15552   {
15553     case EL_EMERALD:
15554     case EL_BD_DIAMOND:
15555     case EL_EMERALD_YELLOW:
15556     case EL_EMERALD_RED:
15557     case EL_EMERALD_PURPLE:
15558     case EL_SP_INFOTRON:
15559       RaiseScore(level.score[SC_EMERALD]);
15560       break;
15561     case EL_DIAMOND:
15562       RaiseScore(level.score[SC_DIAMOND]);
15563       break;
15564     case EL_CRYSTAL:
15565       RaiseScore(level.score[SC_CRYSTAL]);
15566       break;
15567     case EL_PEARL:
15568       RaiseScore(level.score[SC_PEARL]);
15569       break;
15570     case EL_BUG:
15571     case EL_BD_BUTTERFLY:
15572     case EL_SP_ELECTRON:
15573       RaiseScore(level.score[SC_BUG]);
15574       break;
15575     case EL_SPACESHIP:
15576     case EL_BD_FIREFLY:
15577     case EL_SP_SNIKSNAK:
15578       RaiseScore(level.score[SC_SPACESHIP]);
15579       break;
15580     case EL_YAMYAM:
15581     case EL_DARK_YAMYAM:
15582       RaiseScore(level.score[SC_YAMYAM]);
15583       break;
15584     case EL_ROBOT:
15585       RaiseScore(level.score[SC_ROBOT]);
15586       break;
15587     case EL_PACMAN:
15588       RaiseScore(level.score[SC_PACMAN]);
15589       break;
15590     case EL_NUT:
15591       RaiseScore(level.score[SC_NUT]);
15592       break;
15593     case EL_DYNAMITE:
15594     case EL_EM_DYNAMITE:
15595     case EL_SP_DISK_RED:
15596     case EL_DYNABOMB_INCREASE_NUMBER:
15597     case EL_DYNABOMB_INCREASE_SIZE:
15598     case EL_DYNABOMB_INCREASE_POWER:
15599       RaiseScore(level.score[SC_DYNAMITE]);
15600       break;
15601     case EL_SHIELD_NORMAL:
15602     case EL_SHIELD_DEADLY:
15603       RaiseScore(level.score[SC_SHIELD]);
15604       break;
15605     case EL_EXTRA_TIME:
15606       RaiseScore(level.extra_time_score);
15607       break;
15608     case EL_KEY_1:
15609     case EL_KEY_2:
15610     case EL_KEY_3:
15611     case EL_KEY_4:
15612     case EL_EM_KEY_1:
15613     case EL_EM_KEY_2:
15614     case EL_EM_KEY_3:
15615     case EL_EM_KEY_4:
15616     case EL_EMC_KEY_5:
15617     case EL_EMC_KEY_6:
15618     case EL_EMC_KEY_7:
15619     case EL_EMC_KEY_8:
15620     case EL_DC_KEY_WHITE:
15621       RaiseScore(level.score[SC_KEY]);
15622       break;
15623     default:
15624       RaiseScore(element_info[element].collect_score);
15625       break;
15626   }
15627 }
15628
15629 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15630 {
15631   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15632   {
15633     if (!quick_quit)
15634     {
15635       // prevent short reactivation of overlay buttons while closing door
15636       SetOverlayActive(FALSE);
15637       UnmapGameButtons();
15638
15639       // door may still be open due to skipped or envelope style request
15640       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15641     }
15642
15643     if (network.enabled)
15644       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15645     else
15646     {
15647       if (quick_quit)
15648         FadeSkipNextFadeIn();
15649
15650       SetGameStatus(GAME_MODE_MAIN);
15651
15652       DrawMainMenu();
15653     }
15654   }
15655   else          // continue playing the game
15656   {
15657     if (tape.playing && tape.deactivate_display)
15658       TapeDeactivateDisplayOff(TRUE);
15659
15660     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15661
15662     if (tape.playing && tape.deactivate_display)
15663       TapeDeactivateDisplayOn();
15664   }
15665 }
15666
15667 void RequestQuitGame(boolean escape_key_pressed)
15668 {
15669   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15670   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15671                         level_editor_test_game);
15672   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15673                           quick_quit || score_info_tape_play);
15674
15675   RequestQuitGameExt(skip_request, quick_quit,
15676                      "Do you really want to quit the game?");
15677 }
15678
15679 void RequestRestartGame(char *message)
15680 {
15681   game.restart_game_message = NULL;
15682
15683   boolean has_started_game = hasStartedNetworkGame();
15684   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15685
15686   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15687   {
15688     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15689   }
15690   else
15691   {
15692     // needed in case of envelope request to close game panel
15693     CloseDoor(DOOR_CLOSE_1);
15694
15695     SetGameStatus(GAME_MODE_MAIN);
15696
15697     DrawMainMenu();
15698   }
15699 }
15700
15701 void CheckGameOver(void)
15702 {
15703   static boolean last_game_over = FALSE;
15704   static int game_over_delay = 0;
15705   int game_over_delay_value = 50;
15706   boolean game_over = checkGameFailed();
15707
15708   // do not handle game over if request dialog is already active
15709   if (game.request_active)
15710     return;
15711
15712   // do not ask to play again if game was never actually played
15713   if (!game.GamePlayed)
15714     return;
15715
15716   if (!game_over)
15717   {
15718     last_game_over = FALSE;
15719     game_over_delay = game_over_delay_value;
15720
15721     return;
15722   }
15723
15724   if (game_over_delay > 0)
15725   {
15726     game_over_delay--;
15727
15728     return;
15729   }
15730
15731   if (last_game_over != game_over)
15732     game.restart_game_message = (hasStartedNetworkGame() ?
15733                                  "Game over! Play it again?" :
15734                                  "Game over!");
15735
15736   last_game_over = game_over;
15737 }
15738
15739 boolean checkGameSolved(void)
15740 {
15741   // set for all game engines if level was solved
15742   return game.LevelSolved_GameEnd;
15743 }
15744
15745 boolean checkGameFailed(void)
15746 {
15747   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15748     return (game_em.game_over && !game_em.level_solved);
15749   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15750     return (game_sp.game_over && !game_sp.level_solved);
15751   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15752     return (game_mm.game_over && !game_mm.level_solved);
15753   else                          // GAME_ENGINE_TYPE_RND
15754     return (game.GameOver && !game.LevelSolved);
15755 }
15756
15757 boolean checkGameEnded(void)
15758 {
15759   return (checkGameSolved() || checkGameFailed());
15760 }
15761
15762
15763 // ----------------------------------------------------------------------------
15764 // random generator functions
15765 // ----------------------------------------------------------------------------
15766
15767 unsigned int InitEngineRandom_RND(int seed)
15768 {
15769   game.num_random_calls = 0;
15770
15771   return InitEngineRandom(seed);
15772 }
15773
15774 unsigned int RND(int max)
15775 {
15776   if (max > 0)
15777   {
15778     game.num_random_calls++;
15779
15780     return GetEngineRandom(max);
15781   }
15782
15783   return 0;
15784 }
15785
15786
15787 // ----------------------------------------------------------------------------
15788 // game engine snapshot handling functions
15789 // ----------------------------------------------------------------------------
15790
15791 struct EngineSnapshotInfo
15792 {
15793   // runtime values for custom element collect score
15794   int collect_score[NUM_CUSTOM_ELEMENTS];
15795
15796   // runtime values for group element choice position
15797   int choice_pos[NUM_GROUP_ELEMENTS];
15798
15799   // runtime values for belt position animations
15800   int belt_graphic[4][NUM_BELT_PARTS];
15801   int belt_anim_mode[4][NUM_BELT_PARTS];
15802 };
15803
15804 static struct EngineSnapshotInfo engine_snapshot_rnd;
15805 static char *snapshot_level_identifier = NULL;
15806 static int snapshot_level_nr = -1;
15807
15808 static void SaveEngineSnapshotValues_RND(void)
15809 {
15810   static int belt_base_active_element[4] =
15811   {
15812     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15813     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15814     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15815     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15816   };
15817   int i, j;
15818
15819   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15820   {
15821     int element = EL_CUSTOM_START + i;
15822
15823     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15824   }
15825
15826   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15827   {
15828     int element = EL_GROUP_START + i;
15829
15830     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15831   }
15832
15833   for (i = 0; i < 4; i++)
15834   {
15835     for (j = 0; j < NUM_BELT_PARTS; j++)
15836     {
15837       int element = belt_base_active_element[i] + j;
15838       int graphic = el2img(element);
15839       int anim_mode = graphic_info[graphic].anim_mode;
15840
15841       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15842       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15843     }
15844   }
15845 }
15846
15847 static void LoadEngineSnapshotValues_RND(void)
15848 {
15849   unsigned int num_random_calls = game.num_random_calls;
15850   int i, j;
15851
15852   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15853   {
15854     int element = EL_CUSTOM_START + i;
15855
15856     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15857   }
15858
15859   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15860   {
15861     int element = EL_GROUP_START + i;
15862
15863     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15864   }
15865
15866   for (i = 0; i < 4; i++)
15867   {
15868     for (j = 0; j < NUM_BELT_PARTS; j++)
15869     {
15870       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15871       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15872
15873       graphic_info[graphic].anim_mode = anim_mode;
15874     }
15875   }
15876
15877   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15878   {
15879     InitRND(tape.random_seed);
15880     for (i = 0; i < num_random_calls; i++)
15881       RND(1);
15882   }
15883
15884   if (game.num_random_calls != num_random_calls)
15885   {
15886     Error("number of random calls out of sync");
15887     Error("number of random calls should be %d", num_random_calls);
15888     Error("number of random calls is %d", game.num_random_calls);
15889
15890     Fail("this should not happen -- please debug");
15891   }
15892 }
15893
15894 void FreeEngineSnapshotSingle(void)
15895 {
15896   FreeSnapshotSingle();
15897
15898   setString(&snapshot_level_identifier, NULL);
15899   snapshot_level_nr = -1;
15900 }
15901
15902 void FreeEngineSnapshotList(void)
15903 {
15904   FreeSnapshotList();
15905 }
15906
15907 static ListNode *SaveEngineSnapshotBuffers(void)
15908 {
15909   ListNode *buffers = NULL;
15910
15911   // copy some special values to a structure better suited for the snapshot
15912
15913   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15914     SaveEngineSnapshotValues_RND();
15915   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15916     SaveEngineSnapshotValues_EM();
15917   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15918     SaveEngineSnapshotValues_SP(&buffers);
15919   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15920     SaveEngineSnapshotValues_MM(&buffers);
15921
15922   // save values stored in special snapshot structure
15923
15924   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15925     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15926   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15927     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15928   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15929     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15930   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15931     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15932
15933   // save further RND engine values
15934
15935   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15936   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15937   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15938
15939   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15940   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15941   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15942   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15943   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15944
15945   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15946   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15947   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15948
15949   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15950
15951   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15952   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15953
15954   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15955   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15956   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15957   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15958   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15959   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15960   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15961   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15962   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15963   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15964   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15965   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15966   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15967   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15968   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15969   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15970   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15971   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15972
15973   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15974   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15975
15976   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15977   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15978   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15979
15980   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15981   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15982
15983   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15984   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15985   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
15986   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15987   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15988   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15989
15990   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15991   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15992
15993 #if 0
15994   ListNode *node = engine_snapshot_list_rnd;
15995   int num_bytes = 0;
15996
15997   while (node != NULL)
15998   {
15999     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16000
16001     node = node->next;
16002   }
16003
16004   Debug("game:playing:SaveEngineSnapshotBuffers",
16005         "size of engine snapshot: %d bytes", num_bytes);
16006 #endif
16007
16008   return buffers;
16009 }
16010
16011 void SaveEngineSnapshotSingle(void)
16012 {
16013   ListNode *buffers = SaveEngineSnapshotBuffers();
16014
16015   // finally save all snapshot buffers to single snapshot
16016   SaveSnapshotSingle(buffers);
16017
16018   // save level identification information
16019   setString(&snapshot_level_identifier, leveldir_current->identifier);
16020   snapshot_level_nr = level_nr;
16021 }
16022
16023 boolean CheckSaveEngineSnapshotToList(void)
16024 {
16025   boolean save_snapshot =
16026     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16027      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16028       game.snapshot.changed_action) ||
16029      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16030       game.snapshot.collected_item));
16031
16032   game.snapshot.changed_action = FALSE;
16033   game.snapshot.collected_item = FALSE;
16034   game.snapshot.save_snapshot = save_snapshot;
16035
16036   return save_snapshot;
16037 }
16038
16039 void SaveEngineSnapshotToList(void)
16040 {
16041   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16042       tape.quick_resume)
16043     return;
16044
16045   ListNode *buffers = SaveEngineSnapshotBuffers();
16046
16047   // finally save all snapshot buffers to snapshot list
16048   SaveSnapshotToList(buffers);
16049 }
16050
16051 void SaveEngineSnapshotToListInitial(void)
16052 {
16053   FreeEngineSnapshotList();
16054
16055   SaveEngineSnapshotToList();
16056 }
16057
16058 static void LoadEngineSnapshotValues(void)
16059 {
16060   // restore special values from snapshot structure
16061
16062   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16063     LoadEngineSnapshotValues_RND();
16064   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16065     LoadEngineSnapshotValues_EM();
16066   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16067     LoadEngineSnapshotValues_SP();
16068   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16069     LoadEngineSnapshotValues_MM();
16070 }
16071
16072 void LoadEngineSnapshotSingle(void)
16073 {
16074   LoadSnapshotSingle();
16075
16076   LoadEngineSnapshotValues();
16077 }
16078
16079 static void LoadEngineSnapshot_Undo(int steps)
16080 {
16081   LoadSnapshotFromList_Older(steps);
16082
16083   LoadEngineSnapshotValues();
16084 }
16085
16086 static void LoadEngineSnapshot_Redo(int steps)
16087 {
16088   LoadSnapshotFromList_Newer(steps);
16089
16090   LoadEngineSnapshotValues();
16091 }
16092
16093 boolean CheckEngineSnapshotSingle(void)
16094 {
16095   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16096           snapshot_level_nr == level_nr);
16097 }
16098
16099 boolean CheckEngineSnapshotList(void)
16100 {
16101   return CheckSnapshotList();
16102 }
16103
16104
16105 // ---------- new game button stuff -------------------------------------------
16106
16107 static struct
16108 {
16109   int graphic;
16110   struct XY *pos;
16111   int gadget_id;
16112   boolean *setup_value;
16113   boolean allowed_on_tape;
16114   boolean is_touch_button;
16115   char *infotext;
16116 } gamebutton_info[NUM_GAME_BUTTONS] =
16117 {
16118   {
16119     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16120     GAME_CTRL_ID_STOP,                          NULL,
16121     TRUE, FALSE,                                "stop game"
16122   },
16123   {
16124     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16125     GAME_CTRL_ID_PAUSE,                         NULL,
16126     TRUE, FALSE,                                "pause game"
16127   },
16128   {
16129     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16130     GAME_CTRL_ID_PLAY,                          NULL,
16131     TRUE, FALSE,                                "play game"
16132   },
16133   {
16134     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16135     GAME_CTRL_ID_UNDO,                          NULL,
16136     TRUE, FALSE,                                "undo step"
16137   },
16138   {
16139     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16140     GAME_CTRL_ID_REDO,                          NULL,
16141     TRUE, FALSE,                                "redo step"
16142   },
16143   {
16144     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16145     GAME_CTRL_ID_SAVE,                          NULL,
16146     TRUE, FALSE,                                "save game"
16147   },
16148   {
16149     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16150     GAME_CTRL_ID_PAUSE2,                        NULL,
16151     TRUE, FALSE,                                "pause game"
16152   },
16153   {
16154     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16155     GAME_CTRL_ID_LOAD,                          NULL,
16156     TRUE, FALSE,                                "load game"
16157   },
16158   {
16159     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16160     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16161     FALSE, FALSE,                               "stop game"
16162   },
16163   {
16164     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16165     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16166     FALSE, FALSE,                               "pause game"
16167   },
16168   {
16169     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16170     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16171     FALSE, FALSE,                               "play game"
16172   },
16173   {
16174     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16175     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16176     FALSE, TRUE,                                "stop game"
16177   },
16178   {
16179     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16180     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16181     FALSE, TRUE,                                "pause game"
16182   },
16183   {
16184     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16185     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16186     TRUE, FALSE,                                "background music on/off"
16187   },
16188   {
16189     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16190     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16191     TRUE, FALSE,                                "sound loops on/off"
16192   },
16193   {
16194     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16195     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16196     TRUE, FALSE,                                "normal sounds on/off"
16197   },
16198   {
16199     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16200     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16201     FALSE, FALSE,                               "background music on/off"
16202   },
16203   {
16204     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16205     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16206     FALSE, FALSE,                               "sound loops on/off"
16207   },
16208   {
16209     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16210     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16211     FALSE, FALSE,                               "normal sounds on/off"
16212   }
16213 };
16214
16215 void CreateGameButtons(void)
16216 {
16217   int i;
16218
16219   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16220   {
16221     int graphic = gamebutton_info[i].graphic;
16222     struct GraphicInfo *gfx = &graphic_info[graphic];
16223     struct XY *pos = gamebutton_info[i].pos;
16224     struct GadgetInfo *gi;
16225     int button_type;
16226     boolean checked;
16227     unsigned int event_mask;
16228     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16229     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16230     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16231     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16232     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16233     int gd_x   = gfx->src_x;
16234     int gd_y   = gfx->src_y;
16235     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16236     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16237     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16238     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16239     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16240     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16241     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16242     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16243     int id = i;
16244
16245     // do not use touch buttons if overlay touch buttons are disabled
16246     if (is_touch_button && !setup.touch.overlay_buttons)
16247       continue;
16248
16249     if (gfx->bitmap == NULL)
16250     {
16251       game_gadget[id] = NULL;
16252
16253       continue;
16254     }
16255
16256     if (id == GAME_CTRL_ID_STOP ||
16257         id == GAME_CTRL_ID_PANEL_STOP ||
16258         id == GAME_CTRL_ID_TOUCH_STOP ||
16259         id == GAME_CTRL_ID_PLAY ||
16260         id == GAME_CTRL_ID_PANEL_PLAY ||
16261         id == GAME_CTRL_ID_SAVE ||
16262         id == GAME_CTRL_ID_LOAD)
16263     {
16264       button_type = GD_TYPE_NORMAL_BUTTON;
16265       checked = FALSE;
16266       event_mask = GD_EVENT_RELEASED;
16267     }
16268     else if (id == GAME_CTRL_ID_UNDO ||
16269              id == GAME_CTRL_ID_REDO)
16270     {
16271       button_type = GD_TYPE_NORMAL_BUTTON;
16272       checked = FALSE;
16273       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16274     }
16275     else
16276     {
16277       button_type = GD_TYPE_CHECK_BUTTON;
16278       checked = (gamebutton_info[i].setup_value != NULL ?
16279                  *gamebutton_info[i].setup_value : FALSE);
16280       event_mask = GD_EVENT_PRESSED;
16281     }
16282
16283     gi = CreateGadget(GDI_CUSTOM_ID, id,
16284                       GDI_IMAGE_ID, graphic,
16285                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16286                       GDI_X, base_x + x,
16287                       GDI_Y, base_y + y,
16288                       GDI_WIDTH, gfx->width,
16289                       GDI_HEIGHT, gfx->height,
16290                       GDI_TYPE, button_type,
16291                       GDI_STATE, GD_BUTTON_UNPRESSED,
16292                       GDI_CHECKED, checked,
16293                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16294                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16295                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16296                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16297                       GDI_DIRECT_DRAW, FALSE,
16298                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16299                       GDI_EVENT_MASK, event_mask,
16300                       GDI_CALLBACK_ACTION, HandleGameButtons,
16301                       GDI_END);
16302
16303     if (gi == NULL)
16304       Fail("cannot create gadget");
16305
16306     game_gadget[id] = gi;
16307   }
16308 }
16309
16310 void FreeGameButtons(void)
16311 {
16312   int i;
16313
16314   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16315     FreeGadget(game_gadget[i]);
16316 }
16317
16318 static void UnmapGameButtonsAtSamePosition(int id)
16319 {
16320   int i;
16321
16322   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16323     if (i != id &&
16324         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16325         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16326       UnmapGadget(game_gadget[i]);
16327 }
16328
16329 static void UnmapGameButtonsAtSamePosition_All(void)
16330 {
16331   if (setup.show_load_save_buttons)
16332   {
16333     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16334     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16335     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16336   }
16337   else if (setup.show_undo_redo_buttons)
16338   {
16339     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16340     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16341     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16342   }
16343   else
16344   {
16345     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16346     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16347     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16348
16349     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16350     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16351     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16352   }
16353 }
16354
16355 void MapLoadSaveButtons(void)
16356 {
16357   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16358   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16359
16360   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16361   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16362 }
16363
16364 void MapUndoRedoButtons(void)
16365 {
16366   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16367   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16368
16369   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16370   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16371 }
16372
16373 void ModifyPauseButtons(void)
16374 {
16375   static int ids[] =
16376   {
16377     GAME_CTRL_ID_PAUSE,
16378     GAME_CTRL_ID_PAUSE2,
16379     GAME_CTRL_ID_PANEL_PAUSE,
16380     GAME_CTRL_ID_TOUCH_PAUSE,
16381     -1
16382   };
16383   int i;
16384
16385   for (i = 0; ids[i] > -1; i++)
16386     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16387 }
16388
16389 static void MapGameButtonsExt(boolean on_tape)
16390 {
16391   int i;
16392
16393   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16394   {
16395     if ((i == GAME_CTRL_ID_UNDO ||
16396          i == GAME_CTRL_ID_REDO) &&
16397         game_status != GAME_MODE_PLAYING)
16398       continue;
16399
16400     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16401       MapGadget(game_gadget[i]);
16402   }
16403
16404   UnmapGameButtonsAtSamePosition_All();
16405
16406   RedrawGameButtons();
16407 }
16408
16409 static void UnmapGameButtonsExt(boolean on_tape)
16410 {
16411   int i;
16412
16413   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16414     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16415       UnmapGadget(game_gadget[i]);
16416 }
16417
16418 static void RedrawGameButtonsExt(boolean on_tape)
16419 {
16420   int i;
16421
16422   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16423     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16424       RedrawGadget(game_gadget[i]);
16425 }
16426
16427 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16428 {
16429   if (gi == NULL)
16430     return;
16431
16432   gi->checked = state;
16433 }
16434
16435 static void RedrawSoundButtonGadget(int id)
16436 {
16437   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16438              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16439              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16440              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16441              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16442              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16443              id);
16444
16445   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16446   RedrawGadget(game_gadget[id2]);
16447 }
16448
16449 void MapGameButtons(void)
16450 {
16451   MapGameButtonsExt(FALSE);
16452 }
16453
16454 void UnmapGameButtons(void)
16455 {
16456   UnmapGameButtonsExt(FALSE);
16457 }
16458
16459 void RedrawGameButtons(void)
16460 {
16461   RedrawGameButtonsExt(FALSE);
16462 }
16463
16464 void MapGameButtonsOnTape(void)
16465 {
16466   MapGameButtonsExt(TRUE);
16467 }
16468
16469 void UnmapGameButtonsOnTape(void)
16470 {
16471   UnmapGameButtonsExt(TRUE);
16472 }
16473
16474 void RedrawGameButtonsOnTape(void)
16475 {
16476   RedrawGameButtonsExt(TRUE);
16477 }
16478
16479 static void GameUndoRedoExt(void)
16480 {
16481   ClearPlayerAction();
16482
16483   tape.pausing = TRUE;
16484
16485   RedrawPlayfield();
16486   UpdateAndDisplayGameControlValues();
16487
16488   DrawCompleteVideoDisplay();
16489   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16490   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16491   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16492
16493   ModifyPauseButtons();
16494
16495   BackToFront();
16496 }
16497
16498 static void GameUndo(int steps)
16499 {
16500   if (!CheckEngineSnapshotList())
16501     return;
16502
16503   int tape_property_bits = tape.property_bits;
16504
16505   LoadEngineSnapshot_Undo(steps);
16506
16507   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16508
16509   GameUndoRedoExt();
16510 }
16511
16512 static void GameRedo(int steps)
16513 {
16514   if (!CheckEngineSnapshotList())
16515     return;
16516
16517   int tape_property_bits = tape.property_bits;
16518
16519   LoadEngineSnapshot_Redo(steps);
16520
16521   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16522
16523   GameUndoRedoExt();
16524 }
16525
16526 static void HandleGameButtonsExt(int id, int button)
16527 {
16528   static boolean game_undo_executed = FALSE;
16529   int steps = BUTTON_STEPSIZE(button);
16530   boolean handle_game_buttons =
16531     (game_status == GAME_MODE_PLAYING ||
16532      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16533
16534   if (!handle_game_buttons)
16535     return;
16536
16537   switch (id)
16538   {
16539     case GAME_CTRL_ID_STOP:
16540     case GAME_CTRL_ID_PANEL_STOP:
16541     case GAME_CTRL_ID_TOUCH_STOP:
16542       TapeStopGame();
16543
16544       break;
16545
16546     case GAME_CTRL_ID_PAUSE:
16547     case GAME_CTRL_ID_PAUSE2:
16548     case GAME_CTRL_ID_PANEL_PAUSE:
16549     case GAME_CTRL_ID_TOUCH_PAUSE:
16550       if (network.enabled && game_status == GAME_MODE_PLAYING)
16551       {
16552         if (tape.pausing)
16553           SendToServer_ContinuePlaying();
16554         else
16555           SendToServer_PausePlaying();
16556       }
16557       else
16558         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16559
16560       game_undo_executed = FALSE;
16561
16562       break;
16563
16564     case GAME_CTRL_ID_PLAY:
16565     case GAME_CTRL_ID_PANEL_PLAY:
16566       if (game_status == GAME_MODE_MAIN)
16567       {
16568         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16569       }
16570       else if (tape.pausing)
16571       {
16572         if (network.enabled)
16573           SendToServer_ContinuePlaying();
16574         else
16575           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16576       }
16577       break;
16578
16579     case GAME_CTRL_ID_UNDO:
16580       // Important: When using "save snapshot when collecting an item" mode,
16581       // load last (current) snapshot for first "undo" after pressing "pause"
16582       // (else the last-but-one snapshot would be loaded, because the snapshot
16583       // pointer already points to the last snapshot when pressing "pause",
16584       // which is fine for "every step/move" mode, but not for "every collect")
16585       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16586           !game_undo_executed)
16587         steps--;
16588
16589       game_undo_executed = TRUE;
16590
16591       GameUndo(steps);
16592       break;
16593
16594     case GAME_CTRL_ID_REDO:
16595       GameRedo(steps);
16596       break;
16597
16598     case GAME_CTRL_ID_SAVE:
16599       TapeQuickSave();
16600       break;
16601
16602     case GAME_CTRL_ID_LOAD:
16603       TapeQuickLoad();
16604       break;
16605
16606     case SOUND_CTRL_ID_MUSIC:
16607     case SOUND_CTRL_ID_PANEL_MUSIC:
16608       if (setup.sound_music)
16609       { 
16610         setup.sound_music = FALSE;
16611
16612         FadeMusic();
16613       }
16614       else if (audio.music_available)
16615       { 
16616         setup.sound = setup.sound_music = TRUE;
16617
16618         SetAudioMode(setup.sound);
16619
16620         if (game_status == GAME_MODE_PLAYING)
16621           PlayLevelMusic();
16622       }
16623
16624       RedrawSoundButtonGadget(id);
16625
16626       break;
16627
16628     case SOUND_CTRL_ID_LOOPS:
16629     case SOUND_CTRL_ID_PANEL_LOOPS:
16630       if (setup.sound_loops)
16631         setup.sound_loops = FALSE;
16632       else if (audio.loops_available)
16633       {
16634         setup.sound = setup.sound_loops = TRUE;
16635
16636         SetAudioMode(setup.sound);
16637       }
16638
16639       RedrawSoundButtonGadget(id);
16640
16641       break;
16642
16643     case SOUND_CTRL_ID_SIMPLE:
16644     case SOUND_CTRL_ID_PANEL_SIMPLE:
16645       if (setup.sound_simple)
16646         setup.sound_simple = FALSE;
16647       else if (audio.sound_available)
16648       {
16649         setup.sound = setup.sound_simple = TRUE;
16650
16651         SetAudioMode(setup.sound);
16652       }
16653
16654       RedrawSoundButtonGadget(id);
16655
16656       break;
16657
16658     default:
16659       break;
16660   }
16661 }
16662
16663 static void HandleGameButtons(struct GadgetInfo *gi)
16664 {
16665   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16666 }
16667
16668 void HandleSoundButtonKeys(Key key)
16669 {
16670   if (key == setup.shortcut.sound_simple)
16671     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16672   else if (key == setup.shortcut.sound_loops)
16673     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16674   else if (key == setup.shortcut.sound_music)
16675     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16676 }