improved (and fixed) adding new score entry
[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 TestIfPlayerTouchesCustomElement(int, int);
1062 static void TestIfElementTouchesCustomElement(int, int);
1063 static void TestIfElementHitsCustomElement(int, int, int);
1064
1065 static void HandleElementChange(int, int, int);
1066 static void ExecuteCustomElementAction(int, int, int, int);
1067 static boolean ChangeElement(int, int, int, int);
1068
1069 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1070 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1072 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1073         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1074 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1075         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1076 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1077         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1078 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1079         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1080
1081 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1082 #define CheckElementChange(x, y, e, te, ev)                             \
1083         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1084 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1085         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1086 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1087         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1088 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1089         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1090
1091 static void PlayLevelSound(int, int, int);
1092 static void PlayLevelSoundNearest(int, int, int);
1093 static void PlayLevelSoundAction(int, int, int);
1094 static void PlayLevelSoundElementAction(int, int, int, int);
1095 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1096 static void PlayLevelSoundActionIfLoop(int, int, int);
1097 static void StopLevelSoundActionIfLoop(int, int, int);
1098 static void PlayLevelMusic(void);
1099 static void FadeLevelSoundsAndMusic(void);
1100
1101 static void HandleGameButtons(struct GadgetInfo *);
1102
1103 int AmoebaNeighbourNr(int, int);
1104 void AmoebaToDiamond(int, int);
1105 void ContinueMoving(int, int);
1106 void Bang(int, int);
1107 void InitMovDir(int, int);
1108 void InitAmoebaNr(int, int);
1109 int NewHighScore(int);
1110
1111 void TestIfGoodThingHitsBadThing(int, int, int);
1112 void TestIfBadThingHitsGoodThing(int, int, int);
1113 void TestIfPlayerTouchesBadThing(int, int);
1114 void TestIfPlayerRunsIntoBadThing(int, int, int);
1115 void TestIfBadThingTouchesPlayer(int, int);
1116 void TestIfBadThingRunsIntoPlayer(int, int, int);
1117 void TestIfFriendTouchesBadThing(int, int);
1118 void TestIfBadThingTouchesFriend(int, int);
1119 void TestIfBadThingTouchesOtherBadThing(int, int);
1120 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1121
1122 void KillPlayer(struct PlayerInfo *);
1123 void BuryPlayer(struct PlayerInfo *);
1124 void RemovePlayer(struct PlayerInfo *);
1125 void ExitPlayer(struct PlayerInfo *);
1126
1127 static int getInvisibleActiveFromInvisibleElement(int);
1128 static int getInvisibleFromInvisibleActiveElement(int);
1129
1130 static void TestFieldAfterSnapping(int, int, int, int, int);
1131
1132 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1133
1134 // for detection of endless loops, caused by custom element programming
1135 // (using maximal playfield width x 10 is just a rough approximation)
1136 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1137
1138 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1139 {                                                                       \
1140   if (recursion_loop_detected)                                          \
1141     return (rc);                                                        \
1142                                                                         \
1143   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1144   {                                                                     \
1145     recursion_loop_detected = TRUE;                                     \
1146     recursion_loop_element = (e);                                       \
1147   }                                                                     \
1148                                                                         \
1149   recursion_loop_depth++;                                               \
1150 }
1151
1152 #define RECURSION_LOOP_DETECTION_END()                                  \
1153 {                                                                       \
1154   recursion_loop_depth--;                                               \
1155 }
1156
1157 static int recursion_loop_depth;
1158 static boolean recursion_loop_detected;
1159 static boolean recursion_loop_element;
1160
1161 static int map_player_action[MAX_PLAYERS];
1162
1163
1164 // ----------------------------------------------------------------------------
1165 // definition of elements that automatically change to other elements after
1166 // a specified time, eventually calling a function when changing
1167 // ----------------------------------------------------------------------------
1168
1169 // forward declaration for changer functions
1170 static void InitBuggyBase(int, int);
1171 static void WarnBuggyBase(int, int);
1172
1173 static void InitTrap(int, int);
1174 static void ActivateTrap(int, int);
1175 static void ChangeActiveTrap(int, int);
1176
1177 static void InitRobotWheel(int, int);
1178 static void RunRobotWheel(int, int);
1179 static void StopRobotWheel(int, int);
1180
1181 static void InitTimegateWheel(int, int);
1182 static void RunTimegateWheel(int, int);
1183
1184 static void InitMagicBallDelay(int, int);
1185 static void ActivateMagicBall(int, int);
1186
1187 struct ChangingElementInfo
1188 {
1189   int element;
1190   int target_element;
1191   int change_delay;
1192   void (*pre_change_function)(int x, int y);
1193   void (*change_function)(int x, int y);
1194   void (*post_change_function)(int x, int y);
1195 };
1196
1197 static struct ChangingElementInfo change_delay_list[] =
1198 {
1199   {
1200     EL_NUT_BREAKING,
1201     EL_EMERALD,
1202     6,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_PEARL_BREAKING,
1209     EL_EMPTY,
1210     8,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EXIT_OPENING,
1217     EL_EXIT_OPEN,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_EXIT_CLOSING,
1225     EL_EXIT_CLOSED,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_STEEL_EXIT_OPENING,
1233     EL_STEEL_EXIT_OPEN,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_STEEL_EXIT_CLOSING,
1241     EL_STEEL_EXIT_CLOSED,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EM_EXIT_OPENING,
1249     EL_EM_EXIT_OPEN,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_EM_EXIT_CLOSING,
1257     EL_EMPTY,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_EM_STEEL_EXIT_OPENING,
1265     EL_EM_STEEL_EXIT_OPEN,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_EM_STEEL_EXIT_CLOSING,
1273     EL_STEELWALL,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_SP_EXIT_OPENING,
1281     EL_SP_EXIT_OPEN,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SP_EXIT_CLOSING,
1289     EL_SP_EXIT_CLOSED,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SWITCHGATE_OPENING,
1297     EL_SWITCHGATE_OPEN,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SWITCHGATE_CLOSING,
1305     EL_SWITCHGATE_CLOSED,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_TIMEGATE_OPENING,
1313     EL_TIMEGATE_OPEN,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319   {
1320     EL_TIMEGATE_CLOSING,
1321     EL_TIMEGATE_CLOSED,
1322     29,
1323     NULL,
1324     NULL,
1325     NULL
1326   },
1327
1328   {
1329     EL_ACID_SPLASH_LEFT,
1330     EL_EMPTY,
1331     8,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_ACID_SPLASH_RIGHT,
1338     EL_EMPTY,
1339     8,
1340     NULL,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_SP_BUGGY_BASE,
1346     EL_SP_BUGGY_BASE_ACTIVATING,
1347     0,
1348     InitBuggyBase,
1349     NULL,
1350     NULL
1351   },
1352   {
1353     EL_SP_BUGGY_BASE_ACTIVATING,
1354     EL_SP_BUGGY_BASE_ACTIVE,
1355     0,
1356     InitBuggyBase,
1357     NULL,
1358     NULL
1359   },
1360   {
1361     EL_SP_BUGGY_BASE_ACTIVE,
1362     EL_SP_BUGGY_BASE,
1363     0,
1364     InitBuggyBase,
1365     WarnBuggyBase,
1366     NULL
1367   },
1368   {
1369     EL_TRAP,
1370     EL_TRAP_ACTIVE,
1371     0,
1372     InitTrap,
1373     NULL,
1374     ActivateTrap
1375   },
1376   {
1377     EL_TRAP_ACTIVE,
1378     EL_TRAP,
1379     31,
1380     NULL,
1381     ChangeActiveTrap,
1382     NULL
1383   },
1384   {
1385     EL_ROBOT_WHEEL_ACTIVE,
1386     EL_ROBOT_WHEEL,
1387     0,
1388     InitRobotWheel,
1389     RunRobotWheel,
1390     StopRobotWheel
1391   },
1392   {
1393     EL_TIMEGATE_SWITCH_ACTIVE,
1394     EL_TIMEGATE_SWITCH,
1395     0,
1396     InitTimegateWheel,
1397     RunTimegateWheel,
1398     NULL
1399   },
1400   {
1401     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1402     EL_DC_TIMEGATE_SWITCH,
1403     0,
1404     InitTimegateWheel,
1405     RunTimegateWheel,
1406     NULL
1407   },
1408   {
1409     EL_EMC_MAGIC_BALL_ACTIVE,
1410     EL_EMC_MAGIC_BALL_ACTIVE,
1411     0,
1412     InitMagicBallDelay,
1413     NULL,
1414     ActivateMagicBall
1415   },
1416   {
1417     EL_EMC_SPRING_BUMPER_ACTIVE,
1418     EL_EMC_SPRING_BUMPER,
1419     8,
1420     NULL,
1421     NULL,
1422     NULL
1423   },
1424   {
1425     EL_DIAGONAL_SHRINKING,
1426     EL_UNDEFINED,
1427     0,
1428     NULL,
1429     NULL,
1430     NULL
1431   },
1432   {
1433     EL_DIAGONAL_GROWING,
1434     EL_UNDEFINED,
1435     0,
1436     NULL,
1437     NULL,
1438     NULL,
1439   },
1440
1441   {
1442     EL_UNDEFINED,
1443     EL_UNDEFINED,
1444     -1,
1445     NULL,
1446     NULL,
1447     NULL
1448   }
1449 };
1450
1451 struct
1452 {
1453   int element;
1454   int push_delay_fixed, push_delay_random;
1455 }
1456 push_delay_list[] =
1457 {
1458   { EL_SPRING,                  0, 0 },
1459   { EL_BALLOON,                 0, 0 },
1460
1461   { EL_SOKOBAN_OBJECT,          2, 0 },
1462   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1463   { EL_SATELLITE,               2, 0 },
1464   { EL_SP_DISK_YELLOW,          2, 0 },
1465
1466   { EL_UNDEFINED,               0, 0 },
1467 };
1468
1469 struct
1470 {
1471   int element;
1472   int move_stepsize;
1473 }
1474 move_stepsize_list[] =
1475 {
1476   { EL_AMOEBA_DROP,             2 },
1477   { EL_AMOEBA_DROPPING,         2 },
1478   { EL_QUICKSAND_FILLING,       1 },
1479   { EL_QUICKSAND_EMPTYING,      1 },
1480   { EL_QUICKSAND_FAST_FILLING,  2 },
1481   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1482   { EL_MAGIC_WALL_FILLING,      2 },
1483   { EL_MAGIC_WALL_EMPTYING,     2 },
1484   { EL_BD_MAGIC_WALL_FILLING,   2 },
1485   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1486   { EL_DC_MAGIC_WALL_FILLING,   2 },
1487   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1488
1489   { EL_UNDEFINED,               0 },
1490 };
1491
1492 struct
1493 {
1494   int element;
1495   int count;
1496 }
1497 collect_count_list[] =
1498 {
1499   { EL_EMERALD,                 1 },
1500   { EL_BD_DIAMOND,              1 },
1501   { EL_EMERALD_YELLOW,          1 },
1502   { EL_EMERALD_RED,             1 },
1503   { EL_EMERALD_PURPLE,          1 },
1504   { EL_DIAMOND,                 3 },
1505   { EL_SP_INFOTRON,             1 },
1506   { EL_PEARL,                   5 },
1507   { EL_CRYSTAL,                 8 },
1508
1509   { EL_UNDEFINED,               0 },
1510 };
1511
1512 struct
1513 {
1514   int element;
1515   int direction;
1516 }
1517 access_direction_list[] =
1518 {
1519   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1520   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1521   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1522   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1525   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1526   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1527   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1528   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1529   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1530
1531   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1532   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1533   { EL_SP_PORT_UP,                                                   MV_DOWN },
1534   { EL_SP_PORT_DOWN,                                         MV_UP           },
1535   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1536   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1537   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1538   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1539   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1540   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1542   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1543   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1544   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1545   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1546   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1547   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1548   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1549   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1550
1551   { EL_UNDEFINED,                       MV_NONE                              }
1552 };
1553
1554 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1555
1556 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1557 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1558 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1559                                  IS_JUST_CHANGING(x, y))
1560
1561 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1562
1563 // static variables for playfield scan mode (scanning forward or backward)
1564 static int playfield_scan_start_x = 0;
1565 static int playfield_scan_start_y = 0;
1566 static int playfield_scan_delta_x = 1;
1567 static int playfield_scan_delta_y = 1;
1568
1569 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1570                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1571                                      (y) += playfield_scan_delta_y)     \
1572                                 for ((x) = playfield_scan_start_x;      \
1573                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1574                                      (x) += playfield_scan_delta_x)
1575
1576 #ifdef DEBUG
1577 void DEBUG_SetMaximumDynamite(void)
1578 {
1579   int i;
1580
1581   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1582     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1583       local_player->inventory_element[local_player->inventory_size++] =
1584         EL_DYNAMITE;
1585 }
1586 #endif
1587
1588 static void InitPlayfieldScanModeVars(void)
1589 {
1590   if (game.use_reverse_scan_direction)
1591   {
1592     playfield_scan_start_x = lev_fieldx - 1;
1593     playfield_scan_start_y = lev_fieldy - 1;
1594
1595     playfield_scan_delta_x = -1;
1596     playfield_scan_delta_y = -1;
1597   }
1598   else
1599   {
1600     playfield_scan_start_x = 0;
1601     playfield_scan_start_y = 0;
1602
1603     playfield_scan_delta_x = 1;
1604     playfield_scan_delta_y = 1;
1605   }
1606 }
1607
1608 static void InitPlayfieldScanMode(int mode)
1609 {
1610   game.use_reverse_scan_direction =
1611     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1612
1613   InitPlayfieldScanModeVars();
1614 }
1615
1616 static int get_move_delay_from_stepsize(int move_stepsize)
1617 {
1618   move_stepsize =
1619     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1620
1621   // make sure that stepsize value is always a power of 2
1622   move_stepsize = (1 << log_2(move_stepsize));
1623
1624   return TILEX / move_stepsize;
1625 }
1626
1627 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1628                                boolean init_game)
1629 {
1630   int player_nr = player->index_nr;
1631   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1632   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1633
1634   // do no immediately change move delay -- the player might just be moving
1635   player->move_delay_value_next = move_delay;
1636
1637   // information if player can move must be set separately
1638   player->cannot_move = cannot_move;
1639
1640   if (init_game)
1641   {
1642     player->move_delay       = game.initial_move_delay[player_nr];
1643     player->move_delay_value = game.initial_move_delay_value[player_nr];
1644
1645     player->move_delay_value_next = -1;
1646
1647     player->move_delay_reset_counter = 0;
1648   }
1649 }
1650
1651 void GetPlayerConfig(void)
1652 {
1653   GameFrameDelay = setup.game_frame_delay;
1654
1655   if (!audio.sound_available)
1656     setup.sound_simple = FALSE;
1657
1658   if (!audio.loops_available)
1659     setup.sound_loops = FALSE;
1660
1661   if (!audio.music_available)
1662     setup.sound_music = FALSE;
1663
1664   if (!video.fullscreen_available)
1665     setup.fullscreen = FALSE;
1666
1667   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1668
1669   SetAudioMode(setup.sound);
1670 }
1671
1672 int GetElementFromGroupElement(int element)
1673 {
1674   if (IS_GROUP_ELEMENT(element))
1675   {
1676     struct ElementGroupInfo *group = element_info[element].group;
1677     int last_anim_random_frame = gfx.anim_random_frame;
1678     int element_pos;
1679
1680     if (group->choice_mode == ANIM_RANDOM)
1681       gfx.anim_random_frame = RND(group->num_elements_resolved);
1682
1683     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1684                                     group->choice_mode, 0,
1685                                     group->choice_pos);
1686
1687     if (group->choice_mode == ANIM_RANDOM)
1688       gfx.anim_random_frame = last_anim_random_frame;
1689
1690     group->choice_pos++;
1691
1692     element = group->element_resolved[element_pos];
1693   }
1694
1695   return element;
1696 }
1697
1698 static void IncrementSokobanFieldsNeeded(void)
1699 {
1700   if (level.sb_fields_needed)
1701     game.sokoban_fields_still_needed++;
1702 }
1703
1704 static void IncrementSokobanObjectsNeeded(void)
1705 {
1706   if (level.sb_objects_needed)
1707     game.sokoban_objects_still_needed++;
1708 }
1709
1710 static void DecrementSokobanFieldsNeeded(void)
1711 {
1712   if (game.sokoban_fields_still_needed > 0)
1713     game.sokoban_fields_still_needed--;
1714 }
1715
1716 static void DecrementSokobanObjectsNeeded(void)
1717 {
1718   if (game.sokoban_objects_still_needed > 0)
1719     game.sokoban_objects_still_needed--;
1720 }
1721
1722 static void InitPlayerField(int x, int y, int element, boolean init_game)
1723 {
1724   if (element == EL_SP_MURPHY)
1725   {
1726     if (init_game)
1727     {
1728       if (stored_player[0].present)
1729       {
1730         Tile[x][y] = EL_SP_MURPHY_CLONE;
1731
1732         return;
1733       }
1734       else
1735       {
1736         stored_player[0].initial_element = element;
1737         stored_player[0].use_murphy = TRUE;
1738
1739         if (!level.use_artwork_element[0])
1740           stored_player[0].artwork_element = EL_SP_MURPHY;
1741       }
1742
1743       Tile[x][y] = EL_PLAYER_1;
1744     }
1745   }
1746
1747   if (init_game)
1748   {
1749     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1750     int jx = player->jx, jy = player->jy;
1751
1752     player->present = TRUE;
1753
1754     player->block_last_field = (element == EL_SP_MURPHY ?
1755                                 level.sp_block_last_field :
1756                                 level.block_last_field);
1757
1758     // ---------- initialize player's last field block delay ------------------
1759
1760     // always start with reliable default value (no adjustment needed)
1761     player->block_delay_adjustment = 0;
1762
1763     // special case 1: in Supaplex, Murphy blocks last field one more frame
1764     if (player->block_last_field && element == EL_SP_MURPHY)
1765       player->block_delay_adjustment = 1;
1766
1767     // special case 2: in game engines before 3.1.1, blocking was different
1768     if (game.use_block_last_field_bug)
1769       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1770
1771     if (!network.enabled || player->connected_network)
1772     {
1773       player->active = TRUE;
1774
1775       // remove potentially duplicate players
1776       if (StorePlayer[jx][jy] == Tile[x][y])
1777         StorePlayer[jx][jy] = 0;
1778
1779       StorePlayer[x][y] = Tile[x][y];
1780
1781 #if DEBUG_INIT_PLAYER
1782       Debug("game:init:player", "- player element %d activated",
1783             player->element_nr);
1784       Debug("game:init:player", "  (local player is %d and currently %s)",
1785             local_player->element_nr,
1786             local_player->active ? "active" : "not active");
1787     }
1788 #endif
1789
1790     Tile[x][y] = EL_EMPTY;
1791
1792     player->jx = player->last_jx = x;
1793     player->jy = player->last_jy = y;
1794   }
1795
1796   // always check if player was just killed and should be reanimated
1797   {
1798     int player_nr = GET_PLAYER_NR(element);
1799     struct PlayerInfo *player = &stored_player[player_nr];
1800
1801     if (player->active && player->killed)
1802       player->reanimated = TRUE; // if player was just killed, reanimate him
1803   }
1804 }
1805
1806 static void InitField(int x, int y, boolean init_game)
1807 {
1808   int element = Tile[x][y];
1809
1810   switch (element)
1811   {
1812     case EL_SP_MURPHY:
1813     case EL_PLAYER_1:
1814     case EL_PLAYER_2:
1815     case EL_PLAYER_3:
1816     case EL_PLAYER_4:
1817       InitPlayerField(x, y, element, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_PLAYER:
1821       element = Tile[x][y] = EL_PLAYER_1;
1822       InitField(x, y, init_game);
1823
1824       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1825       InitField(x, y, init_game);
1826       break;
1827
1828     case EL_SOKOBAN_FIELD_EMPTY:
1829       IncrementSokobanFieldsNeeded();
1830       break;
1831
1832     case EL_SOKOBAN_OBJECT:
1833       IncrementSokobanObjectsNeeded();
1834       break;
1835
1836     case EL_STONEBLOCK:
1837       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1838         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1839       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1840         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1841       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1844         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1845       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1847       break;
1848
1849     case EL_BUG:
1850     case EL_BUG_RIGHT:
1851     case EL_BUG_UP:
1852     case EL_BUG_LEFT:
1853     case EL_BUG_DOWN:
1854     case EL_SPACESHIP:
1855     case EL_SPACESHIP_RIGHT:
1856     case EL_SPACESHIP_UP:
1857     case EL_SPACESHIP_LEFT:
1858     case EL_SPACESHIP_DOWN:
1859     case EL_BD_BUTTERFLY:
1860     case EL_BD_BUTTERFLY_RIGHT:
1861     case EL_BD_BUTTERFLY_UP:
1862     case EL_BD_BUTTERFLY_LEFT:
1863     case EL_BD_BUTTERFLY_DOWN:
1864     case EL_BD_FIREFLY:
1865     case EL_BD_FIREFLY_RIGHT:
1866     case EL_BD_FIREFLY_UP:
1867     case EL_BD_FIREFLY_LEFT:
1868     case EL_BD_FIREFLY_DOWN:
1869     case EL_PACMAN_RIGHT:
1870     case EL_PACMAN_UP:
1871     case EL_PACMAN_LEFT:
1872     case EL_PACMAN_DOWN:
1873     case EL_YAMYAM:
1874     case EL_YAMYAM_LEFT:
1875     case EL_YAMYAM_RIGHT:
1876     case EL_YAMYAM_UP:
1877     case EL_YAMYAM_DOWN:
1878     case EL_DARK_YAMYAM:
1879     case EL_ROBOT:
1880     case EL_PACMAN:
1881     case EL_SP_SNIKSNAK:
1882     case EL_SP_ELECTRON:
1883     case EL_MOLE:
1884     case EL_MOLE_LEFT:
1885     case EL_MOLE_RIGHT:
1886     case EL_MOLE_UP:
1887     case EL_MOLE_DOWN:
1888     case EL_SPRING_LEFT:
1889     case EL_SPRING_RIGHT:
1890       InitMovDir(x, y);
1891       break;
1892
1893     case EL_AMOEBA_FULL:
1894     case EL_BD_AMOEBA:
1895       InitAmoebaNr(x, y);
1896       break;
1897
1898     case EL_AMOEBA_DROP:
1899       if (y == lev_fieldy - 1)
1900       {
1901         Tile[x][y] = EL_AMOEBA_GROWING;
1902         Store[x][y] = EL_AMOEBA_WET;
1903       }
1904       break;
1905
1906     case EL_DYNAMITE_ACTIVE:
1907     case EL_SP_DISK_RED_ACTIVE:
1908     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1909     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1910     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1912       MovDelay[x][y] = 96;
1913       break;
1914
1915     case EL_EM_DYNAMITE_ACTIVE:
1916       MovDelay[x][y] = 32;
1917       break;
1918
1919     case EL_LAMP:
1920       game.lights_still_needed++;
1921       break;
1922
1923     case EL_PENGUIN:
1924       game.friends_still_needed++;
1925       break;
1926
1927     case EL_PIG:
1928     case EL_DRAGON:
1929       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1930       break;
1931
1932     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1933     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1934     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1935     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1944       if (init_game)
1945       {
1946         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1947         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1948         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1949
1950         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1951         {
1952           game.belt_dir[belt_nr] = belt_dir;
1953           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1954         }
1955         else    // more than one switch -- set it like the first switch
1956         {
1957           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1958         }
1959       }
1960       break;
1961
1962     case EL_LIGHT_SWITCH_ACTIVE:
1963       if (init_game)
1964         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1965       break;
1966
1967     case EL_INVISIBLE_STEELWALL:
1968     case EL_INVISIBLE_WALL:
1969     case EL_INVISIBLE_SAND:
1970       if (game.light_time_left > 0 ||
1971           game.lenses_time_left > 0)
1972         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1973       break;
1974
1975     case EL_EMC_MAGIC_BALL:
1976       if (game.ball_active)
1977         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1978       break;
1979
1980     case EL_EMC_MAGIC_BALL_SWITCH:
1981       if (game.ball_active)
1982         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1983       break;
1984
1985     case EL_TRIGGER_PLAYER:
1986     case EL_TRIGGER_ELEMENT:
1987     case EL_TRIGGER_CE_VALUE:
1988     case EL_TRIGGER_CE_SCORE:
1989     case EL_SELF:
1990     case EL_ANY_ELEMENT:
1991     case EL_CURRENT_CE_VALUE:
1992     case EL_CURRENT_CE_SCORE:
1993     case EL_PREV_CE_1:
1994     case EL_PREV_CE_2:
1995     case EL_PREV_CE_3:
1996     case EL_PREV_CE_4:
1997     case EL_PREV_CE_5:
1998     case EL_PREV_CE_6:
1999     case EL_PREV_CE_7:
2000     case EL_PREV_CE_8:
2001     case EL_NEXT_CE_1:
2002     case EL_NEXT_CE_2:
2003     case EL_NEXT_CE_3:
2004     case EL_NEXT_CE_4:
2005     case EL_NEXT_CE_5:
2006     case EL_NEXT_CE_6:
2007     case EL_NEXT_CE_7:
2008     case EL_NEXT_CE_8:
2009       // reference elements should not be used on the playfield
2010       Tile[x][y] = EL_EMPTY;
2011       break;
2012
2013     default:
2014       if (IS_CUSTOM_ELEMENT(element))
2015       {
2016         if (CAN_MOVE(element))
2017           InitMovDir(x, y);
2018
2019         if (!element_info[element].use_last_ce_value || init_game)
2020           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2021       }
2022       else if (IS_GROUP_ELEMENT(element))
2023       {
2024         Tile[x][y] = GetElementFromGroupElement(element);
2025
2026         InitField(x, y, init_game);
2027       }
2028
2029       break;
2030   }
2031
2032   if (!init_game)
2033     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2034 }
2035
2036 static void InitField_WithBug1(int x, int y, boolean init_game)
2037 {
2038   InitField(x, y, init_game);
2039
2040   // not needed to call InitMovDir() -- already done by InitField()!
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(Tile[x][y]))
2043     InitMovDir(x, y);
2044 }
2045
2046 static void InitField_WithBug2(int x, int y, boolean init_game)
2047 {
2048   int old_element = Tile[x][y];
2049
2050   InitField(x, y, init_game);
2051
2052   // not needed to call InitMovDir() -- already done by InitField()!
2053   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054       CAN_MOVE(old_element) &&
2055       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2056     InitMovDir(x, y);
2057
2058   /* this case is in fact a combination of not less than three bugs:
2059      first, it calls InitMovDir() for elements that can move, although this is
2060      already done by InitField(); then, it checks the element that was at this
2061      field _before_ the call to InitField() (which can change it); lastly, it
2062      was not called for "mole with direction" elements, which were treated as
2063      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2064   */
2065 }
2066
2067 static int get_key_element_from_nr(int key_nr)
2068 {
2069   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2070                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2071                           EL_EM_KEY_1 : EL_KEY_1);
2072
2073   return key_base_element + key_nr;
2074 }
2075
2076 static int get_next_dropped_element(struct PlayerInfo *player)
2077 {
2078   return (player->inventory_size > 0 ?
2079           player->inventory_element[player->inventory_size - 1] :
2080           player->inventory_infinite_element != EL_UNDEFINED ?
2081           player->inventory_infinite_element :
2082           player->dynabombs_left > 0 ?
2083           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2084           EL_UNDEFINED);
2085 }
2086
2087 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2088 {
2089   // pos >= 0: get element from bottom of the stack;
2090   // pos <  0: get element from top of the stack
2091
2092   if (pos < 0)
2093   {
2094     int min_inventory_size = -pos;
2095     int inventory_pos = player->inventory_size - min_inventory_size;
2096     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2097
2098     return (player->inventory_size >= min_inventory_size ?
2099             player->inventory_element[inventory_pos] :
2100             player->inventory_infinite_element != EL_UNDEFINED ?
2101             player->inventory_infinite_element :
2102             player->dynabombs_left >= min_dynabombs_left ?
2103             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2104             EL_UNDEFINED);
2105   }
2106   else
2107   {
2108     int min_dynabombs_left = pos + 1;
2109     int min_inventory_size = pos + 1 - player->dynabombs_left;
2110     int inventory_pos = pos - player->dynabombs_left;
2111
2112     return (player->inventory_infinite_element != EL_UNDEFINED ?
2113             player->inventory_infinite_element :
2114             player->dynabombs_left >= min_dynabombs_left ?
2115             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2116             player->inventory_size >= min_inventory_size ?
2117             player->inventory_element[inventory_pos] :
2118             EL_UNDEFINED);
2119   }
2120 }
2121
2122 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2123 {
2124   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2125   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2126   int compare_result;
2127
2128   if (gpo1->sort_priority != gpo2->sort_priority)
2129     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2130   else
2131     compare_result = gpo1->nr - gpo2->nr;
2132
2133   return compare_result;
2134 }
2135
2136 int getPlayerInventorySize(int player_nr)
2137 {
2138   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2139     return game_em.ply[player_nr]->dynamite;
2140   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2141     return game_sp.red_disk_count;
2142   else
2143     return stored_player[player_nr].inventory_size;
2144 }
2145
2146 static void InitGameControlValues(void)
2147 {
2148   int i;
2149
2150   for (i = 0; game_panel_controls[i].nr != -1; i++)
2151   {
2152     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2153     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2154     struct TextPosInfo *pos = gpc->pos;
2155     int nr = gpc->nr;
2156     int type = gpc->type;
2157
2158     if (nr != i)
2159     {
2160       Error("'game_panel_controls' structure corrupted at %d", i);
2161
2162       Fail("this should not happen -- please debug");
2163     }
2164
2165     // force update of game controls after initialization
2166     gpc->value = gpc->last_value = -1;
2167     gpc->frame = gpc->last_frame = -1;
2168     gpc->gfx_frame = -1;
2169
2170     // determine panel value width for later calculation of alignment
2171     if (type == TYPE_INTEGER || type == TYPE_STRING)
2172     {
2173       pos->width = pos->size * getFontWidth(pos->font);
2174       pos->height = getFontHeight(pos->font);
2175     }
2176     else if (type == TYPE_ELEMENT)
2177     {
2178       pos->width = pos->size;
2179       pos->height = pos->size;
2180     }
2181
2182     // fill structure for game panel draw order
2183     gpo->nr = gpc->nr;
2184     gpo->sort_priority = pos->sort_priority;
2185   }
2186
2187   // sort game panel controls according to sort_priority and control number
2188   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2189         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2190 }
2191
2192 static void UpdatePlayfieldElementCount(void)
2193 {
2194   boolean use_element_count = FALSE;
2195   int i, j, x, y;
2196
2197   // first check if it is needed at all to calculate playfield element count
2198   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2199     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2200       use_element_count = TRUE;
2201
2202   if (!use_element_count)
2203     return;
2204
2205   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2206     element_info[i].element_count = 0;
2207
2208   SCAN_PLAYFIELD(x, y)
2209   {
2210     element_info[Tile[x][y]].element_count++;
2211   }
2212
2213   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2214     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2215       if (IS_IN_GROUP(j, i))
2216         element_info[EL_GROUP_START + i].element_count +=
2217           element_info[j].element_count;
2218 }
2219
2220 static void UpdateGameControlValues(void)
2221 {
2222   int i, k;
2223   int time = (game.LevelSolved ?
2224               game.LevelSolved_CountingTime :
2225               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2226               game_em.lev->time :
2227               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2228               game_sp.time_played :
2229               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2230               game_mm.energy_left :
2231               game.no_time_limit ? TimePlayed : TimeLeft);
2232   int score = (game.LevelSolved ?
2233                game.LevelSolved_CountingScore :
2234                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2235                game_em.lev->score :
2236                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2237                game_sp.score :
2238                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2239                game_mm.score :
2240                game.score);
2241   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2242               game_em.lev->gems_needed :
2243               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2244               game_sp.infotrons_still_needed :
2245               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2246               game_mm.kettles_still_needed :
2247               game.gems_still_needed);
2248   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2249                      game_em.lev->gems_needed > 0 :
2250                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2251                      game_sp.infotrons_still_needed > 0 :
2252                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2253                      game_mm.kettles_still_needed > 0 ||
2254                      game_mm.lights_still_needed > 0 :
2255                      game.gems_still_needed > 0 ||
2256                      game.sokoban_fields_still_needed > 0 ||
2257                      game.sokoban_objects_still_needed > 0 ||
2258                      game.lights_still_needed > 0);
2259   int health = (game.LevelSolved ?
2260                 game.LevelSolved_CountingHealth :
2261                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2262                 MM_HEALTH(game_mm.laser_overload_value) :
2263                 game.health);
2264   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2265
2266   UpdatePlayfieldElementCount();
2267
2268   // update game panel control values
2269
2270   // used instead of "level_nr" (for network games)
2271   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2272   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2273
2274   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2275   for (i = 0; i < MAX_NUM_KEYS; i++)
2276     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2277   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2278   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2279
2280   if (game.centered_player_nr == -1)
2281   {
2282     for (i = 0; i < MAX_PLAYERS; i++)
2283     {
2284       // only one player in Supaplex game engine
2285       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2286         break;
2287
2288       for (k = 0; k < MAX_NUM_KEYS; k++)
2289       {
2290         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2291         {
2292           if (game_em.ply[i]->keys & (1 << k))
2293             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294               get_key_element_from_nr(k);
2295         }
2296         else if (stored_player[i].key[k])
2297           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2298             get_key_element_from_nr(k);
2299       }
2300
2301       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2302         getPlayerInventorySize(i);
2303
2304       if (stored_player[i].num_white_keys > 0)
2305         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2306           EL_DC_KEY_WHITE;
2307
2308       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2309         stored_player[i].num_white_keys;
2310     }
2311   }
2312   else
2313   {
2314     int player_nr = game.centered_player_nr;
2315
2316     for (k = 0; k < MAX_NUM_KEYS; k++)
2317     {
2318       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2319       {
2320         if (game_em.ply[player_nr]->keys & (1 << k))
2321           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2322             get_key_element_from_nr(k);
2323       }
2324       else if (stored_player[player_nr].key[k])
2325         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2326           get_key_element_from_nr(k);
2327     }
2328
2329     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2330       getPlayerInventorySize(player_nr);
2331
2332     if (stored_player[player_nr].num_white_keys > 0)
2333       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2334
2335     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2336       stored_player[player_nr].num_white_keys;
2337   }
2338
2339   // re-arrange keys on game panel, if needed or if defined by style settings
2340   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2341   {
2342     int nr = GAME_PANEL_KEY_1 + i;
2343     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2344     struct TextPosInfo *pos = gpc->pos;
2345
2346     // skip check if key is not in the player's inventory
2347     if (gpc->value == EL_EMPTY)
2348       continue;
2349
2350     // check if keys should be arranged on panel from left to right
2351     if (pos->style == STYLE_LEFTMOST_POSITION)
2352     {
2353       // check previous key positions (left from current key)
2354       for (k = 0; k < i; k++)
2355       {
2356         int nr_new = GAME_PANEL_KEY_1 + k;
2357
2358         if (game_panel_controls[nr_new].value == EL_EMPTY)
2359         {
2360           game_panel_controls[nr_new].value = gpc->value;
2361           gpc->value = EL_EMPTY;
2362
2363           break;
2364         }
2365       }
2366     }
2367
2368     // check if "undefined" keys can be placed at some other position
2369     if (pos->x == -1 && pos->y == -1)
2370     {
2371       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2372
2373       // 1st try: display key at the same position as normal or EM keys
2374       if (game_panel_controls[nr_new].value == EL_EMPTY)
2375       {
2376         game_panel_controls[nr_new].value = gpc->value;
2377       }
2378       else
2379       {
2380         // 2nd try: display key at the next free position in the key panel
2381         for (k = 0; k < STD_NUM_KEYS; k++)
2382         {
2383           nr_new = GAME_PANEL_KEY_1 + k;
2384
2385           if (game_panel_controls[nr_new].value == EL_EMPTY)
2386           {
2387             game_panel_controls[nr_new].value = gpc->value;
2388
2389             break;
2390           }
2391         }
2392       }
2393     }
2394   }
2395
2396   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2397   {
2398     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2399       get_inventory_element_from_pos(local_player, i);
2400     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2401       get_inventory_element_from_pos(local_player, -i - 1);
2402   }
2403
2404   game_panel_controls[GAME_PANEL_SCORE].value = score;
2405   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2406
2407   game_panel_controls[GAME_PANEL_TIME].value = time;
2408
2409   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2410   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2411   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2412
2413   if (level.time == 0)
2414     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2415   else
2416     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2417
2418   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2419   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2420
2421   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2422
2423   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2424     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2425      EL_EMPTY);
2426   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2427     local_player->shield_normal_time_left;
2428   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2429     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2430      EL_EMPTY);
2431   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2432     local_player->shield_deadly_time_left;
2433
2434   game_panel_controls[GAME_PANEL_EXIT].value =
2435     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2436
2437   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2438     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2439   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2440     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2441      EL_EMC_MAGIC_BALL_SWITCH);
2442
2443   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2444     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2445   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2446     game.light_time_left;
2447
2448   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2449     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2450   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2451     game.timegate_time_left;
2452
2453   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2454     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2455
2456   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2457     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2458   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2459     game.lenses_time_left;
2460
2461   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2462     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2463   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2464     game.magnify_time_left;
2465
2466   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2467     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2468      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2469      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2470      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2471      EL_BALLOON_SWITCH_NONE);
2472
2473   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2474     local_player->dynabomb_count;
2475   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2476     local_player->dynabomb_size;
2477   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2478     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2479
2480   game_panel_controls[GAME_PANEL_PENGUINS].value =
2481     game.friends_still_needed;
2482
2483   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2484     game.sokoban_objects_still_needed;
2485   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2486     game.sokoban_fields_still_needed;
2487
2488   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2489     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2490
2491   for (i = 0; i < NUM_BELTS; i++)
2492   {
2493     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2494       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2495        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2496     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2497       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2498   }
2499
2500   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2501     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2502   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2503     game.magic_wall_time_left;
2504
2505   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2506     local_player->gravity;
2507
2508   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2509     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2510
2511   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2512     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2513       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2514        game.panel.element[i].id : EL_UNDEFINED);
2515
2516   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2517     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2518       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2519        element_info[game.panel.element_count[i].id].element_count : 0);
2520
2521   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2522     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2523       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2524        element_info[game.panel.ce_score[i].id].collect_score : 0);
2525
2526   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2527     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2528       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2529        element_info[game.panel.ce_score_element[i].id].collect_score :
2530        EL_UNDEFINED);
2531
2532   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2533   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2534   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2535
2536   // update game panel control frames
2537
2538   for (i = 0; game_panel_controls[i].nr != -1; i++)
2539   {
2540     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2541
2542     if (gpc->type == TYPE_ELEMENT)
2543     {
2544       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2545       {
2546         int last_anim_random_frame = gfx.anim_random_frame;
2547         int element = gpc->value;
2548         int graphic = el2panelimg(element);
2549         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2550                                sync_random_frame : INIT_GFX_RANDOM());
2551
2552         if (gpc->value != gpc->last_value)
2553         {
2554           gpc->gfx_frame = 0;
2555           gpc->gfx_random = init_gfx_random;
2556         }
2557         else
2558         {
2559           gpc->gfx_frame++;
2560
2561           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2562               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2563             gpc->gfx_random = init_gfx_random;
2564         }
2565
2566         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2567           gfx.anim_random_frame = gpc->gfx_random;
2568
2569         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2570           gpc->gfx_frame = element_info[element].collect_score;
2571
2572         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2573
2574         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2575           gfx.anim_random_frame = last_anim_random_frame;
2576       }
2577     }
2578     else if (gpc->type == TYPE_GRAPHIC)
2579     {
2580       if (gpc->graphic != IMG_UNDEFINED)
2581       {
2582         int last_anim_random_frame = gfx.anim_random_frame;
2583         int graphic = gpc->graphic;
2584         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2585                                sync_random_frame : INIT_GFX_RANDOM());
2586
2587         if (gpc->value != gpc->last_value)
2588         {
2589           gpc->gfx_frame = 0;
2590           gpc->gfx_random = init_gfx_random;
2591         }
2592         else
2593         {
2594           gpc->gfx_frame++;
2595
2596           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2597               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2598             gpc->gfx_random = init_gfx_random;
2599         }
2600
2601         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2602           gfx.anim_random_frame = gpc->gfx_random;
2603
2604         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2605
2606         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2607           gfx.anim_random_frame = last_anim_random_frame;
2608       }
2609     }
2610   }
2611 }
2612
2613 static void DisplayGameControlValues(void)
2614 {
2615   boolean redraw_panel = FALSE;
2616   int i;
2617
2618   for (i = 0; game_panel_controls[i].nr != -1; i++)
2619   {
2620     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2621
2622     if (PANEL_DEACTIVATED(gpc->pos))
2623       continue;
2624
2625     if (gpc->value == gpc->last_value &&
2626         gpc->frame == gpc->last_frame)
2627       continue;
2628
2629     redraw_panel = TRUE;
2630   }
2631
2632   if (!redraw_panel)
2633     return;
2634
2635   // copy default game door content to main double buffer
2636
2637   // !!! CHECK AGAIN !!!
2638   SetPanelBackground();
2639   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2640   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2641
2642   // redraw game control buttons
2643   RedrawGameButtons();
2644
2645   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2646
2647   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2648   {
2649     int nr = game_panel_order[i].nr;
2650     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2651     struct TextPosInfo *pos = gpc->pos;
2652     int type = gpc->type;
2653     int value = gpc->value;
2654     int frame = gpc->frame;
2655     int size = pos->size;
2656     int font = pos->font;
2657     boolean draw_masked = pos->draw_masked;
2658     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2659
2660     if (PANEL_DEACTIVATED(pos))
2661       continue;
2662
2663     if (pos->class == get_hash_from_key("extra_panel_items") &&
2664         !setup.prefer_extra_panel_items)
2665       continue;
2666
2667     gpc->last_value = value;
2668     gpc->last_frame = frame;
2669
2670     if (type == TYPE_INTEGER)
2671     {
2672       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2673           nr == GAME_PANEL_TIME)
2674       {
2675         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2676
2677         if (use_dynamic_size)           // use dynamic number of digits
2678         {
2679           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2680           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2681           int size2 = size1 + 1;
2682           int font1 = pos->font;
2683           int font2 = pos->font_alt;
2684
2685           size = (value < value_change ? size1 : size2);
2686           font = (value < value_change ? font1 : font2);
2687         }
2688       }
2689
2690       // correct text size if "digits" is zero or less
2691       if (size <= 0)
2692         size = strlen(int2str(value, size));
2693
2694       // dynamically correct text alignment
2695       pos->width = size * getFontWidth(font);
2696
2697       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2698                   int2str(value, size), font, mask_mode);
2699     }
2700     else if (type == TYPE_ELEMENT)
2701     {
2702       int element, graphic;
2703       Bitmap *src_bitmap;
2704       int src_x, src_y;
2705       int width, height;
2706       int dst_x = PANEL_XPOS(pos);
2707       int dst_y = PANEL_YPOS(pos);
2708
2709       if (value != EL_UNDEFINED && value != EL_EMPTY)
2710       {
2711         element = value;
2712         graphic = el2panelimg(value);
2713
2714 #if 0
2715         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2716               element, EL_NAME(element), size);
2717 #endif
2718
2719         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2720           size = TILESIZE;
2721
2722         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2723                               &src_x, &src_y);
2724
2725         width  = graphic_info[graphic].width  * size / TILESIZE;
2726         height = graphic_info[graphic].height * size / TILESIZE;
2727
2728         if (draw_masked)
2729           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2730                            dst_x, dst_y);
2731         else
2732           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2733                      dst_x, dst_y);
2734       }
2735     }
2736     else if (type == TYPE_GRAPHIC)
2737     {
2738       int graphic        = gpc->graphic;
2739       int graphic_active = gpc->graphic_active;
2740       Bitmap *src_bitmap;
2741       int src_x, src_y;
2742       int width, height;
2743       int dst_x = PANEL_XPOS(pos);
2744       int dst_y = PANEL_YPOS(pos);
2745       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2746                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2747
2748       if (graphic != IMG_UNDEFINED && !skip)
2749       {
2750         if (pos->style == STYLE_REVERSE)
2751           value = 100 - value;
2752
2753         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2754
2755         if (pos->direction & MV_HORIZONTAL)
2756         {
2757           width  = graphic_info[graphic_active].width * value / 100;
2758           height = graphic_info[graphic_active].height;
2759
2760           if (pos->direction == MV_LEFT)
2761           {
2762             src_x += graphic_info[graphic_active].width - width;
2763             dst_x += graphic_info[graphic_active].width - width;
2764           }
2765         }
2766         else
2767         {
2768           width  = graphic_info[graphic_active].width;
2769           height = graphic_info[graphic_active].height * value / 100;
2770
2771           if (pos->direction == MV_UP)
2772           {
2773             src_y += graphic_info[graphic_active].height - height;
2774             dst_y += graphic_info[graphic_active].height - height;
2775           }
2776         }
2777
2778         if (draw_masked)
2779           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2780                            dst_x, dst_y);
2781         else
2782           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2783                      dst_x, dst_y);
2784
2785         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2786
2787         if (pos->direction & MV_HORIZONTAL)
2788         {
2789           if (pos->direction == MV_RIGHT)
2790           {
2791             src_x += width;
2792             dst_x += width;
2793           }
2794           else
2795           {
2796             dst_x = PANEL_XPOS(pos);
2797           }
2798
2799           width = graphic_info[graphic].width - width;
2800         }
2801         else
2802         {
2803           if (pos->direction == MV_DOWN)
2804           {
2805             src_y += height;
2806             dst_y += height;
2807           }
2808           else
2809           {
2810             dst_y = PANEL_YPOS(pos);
2811           }
2812
2813           height = graphic_info[graphic].height - height;
2814         }
2815
2816         if (draw_masked)
2817           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2818                            dst_x, dst_y);
2819         else
2820           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2821                      dst_x, dst_y);
2822       }
2823     }
2824     else if (type == TYPE_STRING)
2825     {
2826       boolean active = (value != 0);
2827       char *state_normal = "off";
2828       char *state_active = "on";
2829       char *state = (active ? state_active : state_normal);
2830       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2831                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2832                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2833                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2834
2835       if (nr == GAME_PANEL_GRAVITY_STATE)
2836       {
2837         int font1 = pos->font;          // (used for normal state)
2838         int font2 = pos->font_alt;      // (used for active state)
2839
2840         font = (active ? font2 : font1);
2841       }
2842
2843       if (s != NULL)
2844       {
2845         char *s_cut;
2846
2847         if (size <= 0)
2848         {
2849           // don't truncate output if "chars" is zero or less
2850           size = strlen(s);
2851
2852           // dynamically correct text alignment
2853           pos->width = size * getFontWidth(font);
2854         }
2855
2856         s_cut = getStringCopyN(s, size);
2857
2858         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2859                     s_cut, font, mask_mode);
2860
2861         free(s_cut);
2862       }
2863     }
2864
2865     redraw_mask |= REDRAW_DOOR_1;
2866   }
2867
2868   SetGameStatus(GAME_MODE_PLAYING);
2869 }
2870
2871 void UpdateAndDisplayGameControlValues(void)
2872 {
2873   if (tape.deactivate_display)
2874     return;
2875
2876   UpdateGameControlValues();
2877   DisplayGameControlValues();
2878 }
2879
2880 void UpdateGameDoorValues(void)
2881 {
2882   UpdateGameControlValues();
2883 }
2884
2885 void DrawGameDoorValues(void)
2886 {
2887   DisplayGameControlValues();
2888 }
2889
2890
2891 // ============================================================================
2892 // InitGameEngine()
2893 // ----------------------------------------------------------------------------
2894 // initialize game engine due to level / tape version number
2895 // ============================================================================
2896
2897 static void InitGameEngine(void)
2898 {
2899   int i, j, k, l, x, y;
2900
2901   // set game engine from tape file when re-playing, else from level file
2902   game.engine_version = (tape.playing ? tape.engine_version :
2903                          level.game_version);
2904
2905   // set single or multi-player game mode (needed for re-playing tapes)
2906   game.team_mode = setup.team_mode;
2907
2908   if (tape.playing)
2909   {
2910     int num_players = 0;
2911
2912     for (i = 0; i < MAX_PLAYERS; i++)
2913       if (tape.player_participates[i])
2914         num_players++;
2915
2916     // multi-player tapes contain input data for more than one player
2917     game.team_mode = (num_players > 1);
2918   }
2919
2920 #if 0
2921   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2922         level.game_version);
2923   Debug("game:init:level", "          tape.file_version   == %06d",
2924         tape.file_version);
2925   Debug("game:init:level", "          tape.game_version   == %06d",
2926         tape.game_version);
2927   Debug("game:init:level", "          tape.engine_version == %06d",
2928         tape.engine_version);
2929   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2930         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2931 #endif
2932
2933   // --------------------------------------------------------------------------
2934   // set flags for bugs and changes according to active game engine version
2935   // --------------------------------------------------------------------------
2936
2937   /*
2938     Summary of bugfix:
2939     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2940
2941     Bug was introduced in version:
2942     2.0.1
2943
2944     Bug was fixed in version:
2945     4.2.0.0
2946
2947     Description:
2948     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2949     but the property "can fall" was missing, which caused some levels to be
2950     unsolvable. This was fixed in version 4.2.0.0.
2951
2952     Affected levels/tapes:
2953     An example for a tape that was fixed by this bugfix is tape 029 from the
2954     level set "rnd_sam_bateman".
2955     The wrong behaviour will still be used for all levels or tapes that were
2956     created/recorded with it. An example for this is tape 023 from the level
2957     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2958   */
2959
2960   boolean use_amoeba_dropping_cannot_fall_bug =
2961     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2962       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2963      (tape.playing &&
2964       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2965       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2966
2967   /*
2968     Summary of bugfix/change:
2969     Fixed move speed of elements entering or leaving magic wall.
2970
2971     Fixed/changed in version:
2972     2.0.1
2973
2974     Description:
2975     Before 2.0.1, move speed of elements entering or leaving magic wall was
2976     twice as fast as it is now.
2977     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2978
2979     Affected levels/tapes:
2980     The first condition is generally needed for all levels/tapes before version
2981     2.0.1, which might use the old behaviour before it was changed; known tapes
2982     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2983     The second condition is an exception from the above case and is needed for
2984     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2985     above, but before it was known that this change would break tapes like the
2986     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2987     although the engine version while recording maybe was before 2.0.1. There
2988     are a lot of tapes that are affected by this exception, like tape 006 from
2989     the level set "rnd_conor_mancone".
2990   */
2991
2992   boolean use_old_move_stepsize_for_magic_wall =
2993     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2994      !(tape.playing &&
2995        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2996        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2997
2998   /*
2999     Summary of bugfix/change:
3000     Fixed handling for custom elements that change when pushed by the player.
3001
3002     Fixed/changed in version:
3003     3.1.0
3004
3005     Description:
3006     Before 3.1.0, custom elements that "change when pushing" changed directly
3007     after the player started pushing them (until then handled in "DigField()").
3008     Since 3.1.0, these custom elements are not changed until the "pushing"
3009     move of the element is finished (now handled in "ContinueMoving()").
3010
3011     Affected levels/tapes:
3012     The first condition is generally needed for all levels/tapes before version
3013     3.1.0, which might use the old behaviour before it was changed; known tapes
3014     that are affected are some tapes from the level set "Walpurgis Gardens" by
3015     Jamie Cullen.
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3018     above (including some development versions of 3.1.0), but before it was
3019     known that this change would break tapes like the above and was fixed in
3020     3.1.1, so that the changed behaviour was active although the engine version
3021     while recording maybe was before 3.1.0. There is at least one tape that is
3022     affected by this exception, which is the tape for the one-level set "Bug
3023     Machine" by Juergen Bonhagen.
3024   */
3025
3026   game.use_change_when_pushing_bug =
3027     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3028      !(tape.playing &&
3029        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3030        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3031
3032   /*
3033     Summary of bugfix/change:
3034     Fixed handling for blocking the field the player leaves when moving.
3035
3036     Fixed/changed in version:
3037     3.1.1
3038
3039     Description:
3040     Before 3.1.1, when "block last field when moving" was enabled, the field
3041     the player is leaving when moving was blocked for the time of the move,
3042     and was directly unblocked afterwards. This resulted in the last field
3043     being blocked for exactly one less than the number of frames of one player
3044     move. Additionally, even when blocking was disabled, the last field was
3045     blocked for exactly one frame.
3046     Since 3.1.1, due to changes in player movement handling, the last field
3047     is not blocked at all when blocking is disabled. When blocking is enabled,
3048     the last field is blocked for exactly the number of frames of one player
3049     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3050     last field is blocked for exactly one more than the number of frames of
3051     one player move.
3052
3053     Affected levels/tapes:
3054     (!!! yet to be determined -- probably many !!!)
3055   */
3056
3057   game.use_block_last_field_bug =
3058     (game.engine_version < VERSION_IDENT(3,1,1,0));
3059
3060   /* various special flags and settings for native Emerald Mine game engine */
3061
3062   game_em.use_single_button =
3063     (game.engine_version > VERSION_IDENT(4,0,0,2));
3064
3065   game_em.use_snap_key_bug =
3066     (game.engine_version < VERSION_IDENT(4,0,1,0));
3067
3068   game_em.use_random_bug =
3069     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3070
3071   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3072
3073   game_em.use_old_explosions            = use_old_em_engine;
3074   game_em.use_old_android               = use_old_em_engine;
3075   game_em.use_old_push_elements         = use_old_em_engine;
3076   game_em.use_old_push_into_acid        = use_old_em_engine;
3077
3078   game_em.use_wrap_around               = !use_old_em_engine;
3079
3080   // --------------------------------------------------------------------------
3081
3082   // set maximal allowed number of custom element changes per game frame
3083   game.max_num_changes_per_frame = 1;
3084
3085   // default scan direction: scan playfield from top/left to bottom/right
3086   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3087
3088   // dynamically adjust element properties according to game engine version
3089   InitElementPropertiesEngine(game.engine_version);
3090
3091   // ---------- initialize special element properties -------------------------
3092
3093   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3094   if (use_amoeba_dropping_cannot_fall_bug)
3095     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3096
3097   // ---------- initialize player's initial move delay ------------------------
3098
3099   // dynamically adjust player properties according to level information
3100   for (i = 0; i < MAX_PLAYERS; i++)
3101     game.initial_move_delay_value[i] =
3102       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3103
3104   // dynamically adjust player properties according to game engine version
3105   for (i = 0; i < MAX_PLAYERS; i++)
3106     game.initial_move_delay[i] =
3107       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3108        game.initial_move_delay_value[i] : 0);
3109
3110   // ---------- initialize player's initial push delay ------------------------
3111
3112   // dynamically adjust player properties according to game engine version
3113   game.initial_push_delay_value =
3114     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3115
3116   // ---------- initialize changing elements ----------------------------------
3117
3118   // initialize changing elements information
3119   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3120   {
3121     struct ElementInfo *ei = &element_info[i];
3122
3123     // this pointer might have been changed in the level editor
3124     ei->change = &ei->change_page[0];
3125
3126     if (!IS_CUSTOM_ELEMENT(i))
3127     {
3128       ei->change->target_element = EL_EMPTY_SPACE;
3129       ei->change->delay_fixed = 0;
3130       ei->change->delay_random = 0;
3131       ei->change->delay_frames = 1;
3132     }
3133
3134     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3135     {
3136       ei->has_change_event[j] = FALSE;
3137
3138       ei->event_page_nr[j] = 0;
3139       ei->event_page[j] = &ei->change_page[0];
3140     }
3141   }
3142
3143   // add changing elements from pre-defined list
3144   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3145   {
3146     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3147     struct ElementInfo *ei = &element_info[ch_delay->element];
3148
3149     ei->change->target_element       = ch_delay->target_element;
3150     ei->change->delay_fixed          = ch_delay->change_delay;
3151
3152     ei->change->pre_change_function  = ch_delay->pre_change_function;
3153     ei->change->change_function      = ch_delay->change_function;
3154     ei->change->post_change_function = ch_delay->post_change_function;
3155
3156     ei->change->can_change = TRUE;
3157     ei->change->can_change_or_has_action = TRUE;
3158
3159     ei->has_change_event[CE_DELAY] = TRUE;
3160
3161     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3162     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3163   }
3164
3165   // ---------- initialize internal run-time variables ------------------------
3166
3167   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3168   {
3169     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3170
3171     for (j = 0; j < ei->num_change_pages; j++)
3172     {
3173       ei->change_page[j].can_change_or_has_action =
3174         (ei->change_page[j].can_change |
3175          ei->change_page[j].has_action);
3176     }
3177   }
3178
3179   // add change events from custom element configuration
3180   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3181   {
3182     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3183
3184     for (j = 0; j < ei->num_change_pages; j++)
3185     {
3186       if (!ei->change_page[j].can_change_or_has_action)
3187         continue;
3188
3189       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3190       {
3191         // only add event page for the first page found with this event
3192         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3193         {
3194           ei->has_change_event[k] = TRUE;
3195
3196           ei->event_page_nr[k] = j;
3197           ei->event_page[k] = &ei->change_page[j];
3198         }
3199       }
3200     }
3201   }
3202
3203   // ---------- initialize reference elements in change conditions ------------
3204
3205   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3206   {
3207     int element = EL_CUSTOM_START + i;
3208     struct ElementInfo *ei = &element_info[element];
3209
3210     for (j = 0; j < ei->num_change_pages; j++)
3211     {
3212       int trigger_element = ei->change_page[j].initial_trigger_element;
3213
3214       if (trigger_element >= EL_PREV_CE_8 &&
3215           trigger_element <= EL_NEXT_CE_8)
3216         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3217
3218       ei->change_page[j].trigger_element = trigger_element;
3219     }
3220   }
3221
3222   // ---------- initialize run-time trigger player and element ----------------
3223
3224   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3225   {
3226     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3227
3228     for (j = 0; j < ei->num_change_pages; j++)
3229     {
3230       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3231       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3232       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3233       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3234       ei->change_page[j].actual_trigger_ce_value = 0;
3235       ei->change_page[j].actual_trigger_ce_score = 0;
3236     }
3237   }
3238
3239   // ---------- initialize trigger events -------------------------------------
3240
3241   // initialize trigger events information
3242   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3243     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3244       trigger_events[i][j] = FALSE;
3245
3246   // add trigger events from element change event properties
3247   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3248   {
3249     struct ElementInfo *ei = &element_info[i];
3250
3251     for (j = 0; j < ei->num_change_pages; j++)
3252     {
3253       if (!ei->change_page[j].can_change_or_has_action)
3254         continue;
3255
3256       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3257       {
3258         int trigger_element = ei->change_page[j].trigger_element;
3259
3260         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3261         {
3262           if (ei->change_page[j].has_event[k])
3263           {
3264             if (IS_GROUP_ELEMENT(trigger_element))
3265             {
3266               struct ElementGroupInfo *group =
3267                 element_info[trigger_element].group;
3268
3269               for (l = 0; l < group->num_elements_resolved; l++)
3270                 trigger_events[group->element_resolved[l]][k] = TRUE;
3271             }
3272             else if (trigger_element == EL_ANY_ELEMENT)
3273               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3274                 trigger_events[l][k] = TRUE;
3275             else
3276               trigger_events[trigger_element][k] = TRUE;
3277           }
3278         }
3279       }
3280     }
3281   }
3282
3283   // ---------- initialize push delay -----------------------------------------
3284
3285   // initialize push delay values to default
3286   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3287   {
3288     if (!IS_CUSTOM_ELEMENT(i))
3289     {
3290       // set default push delay values (corrected since version 3.0.7-1)
3291       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3292       {
3293         element_info[i].push_delay_fixed = 2;
3294         element_info[i].push_delay_random = 8;
3295       }
3296       else
3297       {
3298         element_info[i].push_delay_fixed = 8;
3299         element_info[i].push_delay_random = 8;
3300       }
3301     }
3302   }
3303
3304   // set push delay value for certain elements from pre-defined list
3305   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3306   {
3307     int e = push_delay_list[i].element;
3308
3309     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3310     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3311   }
3312
3313   // set push delay value for Supaplex elements for newer engine versions
3314   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3315   {
3316     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3317     {
3318       if (IS_SP_ELEMENT(i))
3319       {
3320         // set SP push delay to just enough to push under a falling zonk
3321         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3322
3323         element_info[i].push_delay_fixed  = delay;
3324         element_info[i].push_delay_random = 0;
3325       }
3326     }
3327   }
3328
3329   // ---------- initialize move stepsize --------------------------------------
3330
3331   // initialize move stepsize values to default
3332   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3333     if (!IS_CUSTOM_ELEMENT(i))
3334       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3335
3336   // set move stepsize value for certain elements from pre-defined list
3337   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3338   {
3339     int e = move_stepsize_list[i].element;
3340
3341     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3342
3343     // set move stepsize value for certain elements for older engine versions
3344     if (use_old_move_stepsize_for_magic_wall)
3345     {
3346       if (e == EL_MAGIC_WALL_FILLING ||
3347           e == EL_MAGIC_WALL_EMPTYING ||
3348           e == EL_BD_MAGIC_WALL_FILLING ||
3349           e == EL_BD_MAGIC_WALL_EMPTYING)
3350         element_info[e].move_stepsize *= 2;
3351     }
3352   }
3353
3354   // ---------- initialize collect score --------------------------------------
3355
3356   // initialize collect score values for custom elements from initial value
3357   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3358     if (IS_CUSTOM_ELEMENT(i))
3359       element_info[i].collect_score = element_info[i].collect_score_initial;
3360
3361   // ---------- initialize collect count --------------------------------------
3362
3363   // initialize collect count values for non-custom elements
3364   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3365     if (!IS_CUSTOM_ELEMENT(i))
3366       element_info[i].collect_count_initial = 0;
3367
3368   // add collect count values for all elements from pre-defined list
3369   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3370     element_info[collect_count_list[i].element].collect_count_initial =
3371       collect_count_list[i].count;
3372
3373   // ---------- initialize access direction -----------------------------------
3374
3375   // initialize access direction values to default (access from every side)
3376   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3377     if (!IS_CUSTOM_ELEMENT(i))
3378       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3379
3380   // set access direction value for certain elements from pre-defined list
3381   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3382     element_info[access_direction_list[i].element].access_direction =
3383       access_direction_list[i].direction;
3384
3385   // ---------- initialize explosion content ----------------------------------
3386   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3387   {
3388     if (IS_CUSTOM_ELEMENT(i))
3389       continue;
3390
3391     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3392     {
3393       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3394
3395       element_info[i].content.e[x][y] =
3396         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3397          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3398          i == EL_PLAYER_3 ? EL_EMERALD :
3399          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3400          i == EL_MOLE ? EL_EMERALD_RED :
3401          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3402          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3403          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3404          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3405          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3406          i == EL_WALL_EMERALD ? EL_EMERALD :
3407          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3408          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3409          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3410          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3411          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3412          i == EL_WALL_PEARL ? EL_PEARL :
3413          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3414          EL_EMPTY);
3415     }
3416   }
3417
3418   // ---------- initialize recursion detection --------------------------------
3419   recursion_loop_depth = 0;
3420   recursion_loop_detected = FALSE;
3421   recursion_loop_element = EL_UNDEFINED;
3422
3423   // ---------- initialize graphics engine ------------------------------------
3424   game.scroll_delay_value =
3425     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3426      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3427      !setup.forced_scroll_delay           ? 0 :
3428      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3429   game.scroll_delay_value =
3430     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3431
3432   // ---------- initialize game engine snapshots ------------------------------
3433   for (i = 0; i < MAX_PLAYERS; i++)
3434     game.snapshot.last_action[i] = 0;
3435   game.snapshot.changed_action = FALSE;
3436   game.snapshot.collected_item = FALSE;
3437   game.snapshot.mode =
3438     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3439      SNAPSHOT_MODE_EVERY_STEP :
3440      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3441      SNAPSHOT_MODE_EVERY_MOVE :
3442      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3443      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3444   game.snapshot.save_snapshot = FALSE;
3445
3446   // ---------- initialize level time for Supaplex engine ---------------------
3447   // Supaplex levels with time limit currently unsupported -- should be added
3448   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3449     level.time = 0;
3450
3451   // ---------- initialize flags for handling game actions --------------------
3452
3453   // set flags for game actions to default values
3454   game.use_key_actions = TRUE;
3455   game.use_mouse_actions = FALSE;
3456
3457   // when using Mirror Magic game engine, handle mouse events only
3458   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3459   {
3460     game.use_key_actions = FALSE;
3461     game.use_mouse_actions = TRUE;
3462   }
3463
3464   // check for custom elements with mouse click events
3465   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3466   {
3467     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3468     {
3469       int element = EL_CUSTOM_START + i;
3470
3471       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3472           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3473           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3474           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3475         game.use_mouse_actions = TRUE;
3476     }
3477   }
3478 }
3479
3480 static int get_num_special_action(int element, int action_first,
3481                                   int action_last)
3482 {
3483   int num_special_action = 0;
3484   int i, j;
3485
3486   for (i = action_first; i <= action_last; i++)
3487   {
3488     boolean found = FALSE;
3489
3490     for (j = 0; j < NUM_DIRECTIONS; j++)
3491       if (el_act_dir2img(element, i, j) !=
3492           el_act_dir2img(element, ACTION_DEFAULT, j))
3493         found = TRUE;
3494
3495     if (found)
3496       num_special_action++;
3497     else
3498       break;
3499   }
3500
3501   return num_special_action;
3502 }
3503
3504
3505 // ============================================================================
3506 // InitGame()
3507 // ----------------------------------------------------------------------------
3508 // initialize and start new game
3509 // ============================================================================
3510
3511 #if DEBUG_INIT_PLAYER
3512 static void DebugPrintPlayerStatus(char *message)
3513 {
3514   int i;
3515
3516   if (!options.debug)
3517     return;
3518
3519   Debug("game:init:player", "%s:", message);
3520
3521   for (i = 0; i < MAX_PLAYERS; i++)
3522   {
3523     struct PlayerInfo *player = &stored_player[i];
3524
3525     Debug("game:init:player",
3526           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3527           i + 1,
3528           player->present,
3529           player->connected,
3530           player->connected_locally,
3531           player->connected_network,
3532           player->active,
3533           (local_player == player ? " (local player)" : ""));
3534   }
3535 }
3536 #endif
3537
3538 void InitGame(void)
3539 {
3540   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3541   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3542   int fade_mask = REDRAW_FIELD;
3543
3544   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3545   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3546   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3547   int initial_move_dir = MV_DOWN;
3548   int i, j, x, y;
3549
3550   // required here to update video display before fading (FIX THIS)
3551   DrawMaskedBorder(REDRAW_DOOR_2);
3552
3553   if (!game.restart_level)
3554     CloseDoor(DOOR_CLOSE_1);
3555
3556   SetGameStatus(GAME_MODE_PLAYING);
3557
3558   if (level_editor_test_game)
3559     FadeSkipNextFadeOut();
3560   else
3561     FadeSetEnterScreen();
3562
3563   if (CheckFadeAll())
3564     fade_mask = REDRAW_ALL;
3565
3566   FadeLevelSoundsAndMusic();
3567
3568   ExpireSoundLoops(TRUE);
3569
3570   FadeOut(fade_mask);
3571
3572   if (level_editor_test_game)
3573     FadeSkipNextFadeIn();
3574
3575   // needed if different viewport properties defined for playing
3576   ChangeViewportPropertiesIfNeeded();
3577
3578   ClearField();
3579
3580   DrawCompleteVideoDisplay();
3581
3582   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3583
3584   InitGameEngine();
3585   InitGameControlValues();
3586
3587   if (tape.recording)
3588   {
3589     // initialize tape actions from game when recording tape
3590     tape.use_key_actions   = game.use_key_actions;
3591     tape.use_mouse_actions = game.use_mouse_actions;
3592
3593     // initialize visible playfield size when recording tape (for team mode)
3594     tape.scr_fieldx = SCR_FIELDX;
3595     tape.scr_fieldy = SCR_FIELDY;
3596   }
3597
3598   // don't play tapes over network
3599   network_playing = (network.enabled && !tape.playing);
3600
3601   for (i = 0; i < MAX_PLAYERS; i++)
3602   {
3603     struct PlayerInfo *player = &stored_player[i];
3604
3605     player->index_nr = i;
3606     player->index_bit = (1 << i);
3607     player->element_nr = EL_PLAYER_1 + i;
3608
3609     player->present = FALSE;
3610     player->active = FALSE;
3611     player->mapped = FALSE;
3612
3613     player->killed = FALSE;
3614     player->reanimated = FALSE;
3615     player->buried = FALSE;
3616
3617     player->action = 0;
3618     player->effective_action = 0;
3619     player->programmed_action = 0;
3620     player->snap_action = 0;
3621
3622     player->mouse_action.lx = 0;
3623     player->mouse_action.ly = 0;
3624     player->mouse_action.button = 0;
3625     player->mouse_action.button_hint = 0;
3626
3627     player->effective_mouse_action.lx = 0;
3628     player->effective_mouse_action.ly = 0;
3629     player->effective_mouse_action.button = 0;
3630     player->effective_mouse_action.button_hint = 0;
3631
3632     for (j = 0; j < MAX_NUM_KEYS; j++)
3633       player->key[j] = FALSE;
3634
3635     player->num_white_keys = 0;
3636
3637     player->dynabomb_count = 0;
3638     player->dynabomb_size = 1;
3639     player->dynabombs_left = 0;
3640     player->dynabomb_xl = FALSE;
3641
3642     player->MovDir = initial_move_dir;
3643     player->MovPos = 0;
3644     player->GfxPos = 0;
3645     player->GfxDir = initial_move_dir;
3646     player->GfxAction = ACTION_DEFAULT;
3647     player->Frame = 0;
3648     player->StepFrame = 0;
3649
3650     player->initial_element = player->element_nr;
3651     player->artwork_element =
3652       (level.use_artwork_element[i] ? level.artwork_element[i] :
3653        player->element_nr);
3654     player->use_murphy = FALSE;
3655
3656     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3657     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3658
3659     player->gravity = level.initial_player_gravity[i];
3660
3661     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3662
3663     player->actual_frame_counter = 0;
3664
3665     player->step_counter = 0;
3666
3667     player->last_move_dir = initial_move_dir;
3668
3669     player->is_active = FALSE;
3670
3671     player->is_waiting = FALSE;
3672     player->is_moving = FALSE;
3673     player->is_auto_moving = FALSE;
3674     player->is_digging = FALSE;
3675     player->is_snapping = FALSE;
3676     player->is_collecting = FALSE;
3677     player->is_pushing = FALSE;
3678     player->is_switching = FALSE;
3679     player->is_dropping = FALSE;
3680     player->is_dropping_pressed = FALSE;
3681
3682     player->is_bored = FALSE;
3683     player->is_sleeping = FALSE;
3684
3685     player->was_waiting = TRUE;
3686     player->was_moving = FALSE;
3687     player->was_snapping = FALSE;
3688     player->was_dropping = FALSE;
3689
3690     player->force_dropping = FALSE;
3691
3692     player->frame_counter_bored = -1;
3693     player->frame_counter_sleeping = -1;
3694
3695     player->anim_delay_counter = 0;
3696     player->post_delay_counter = 0;
3697
3698     player->dir_waiting = initial_move_dir;
3699     player->action_waiting = ACTION_DEFAULT;
3700     player->last_action_waiting = ACTION_DEFAULT;
3701     player->special_action_bored = ACTION_DEFAULT;
3702     player->special_action_sleeping = ACTION_DEFAULT;
3703
3704     player->switch_x = -1;
3705     player->switch_y = -1;
3706
3707     player->drop_x = -1;
3708     player->drop_y = -1;
3709
3710     player->show_envelope = 0;
3711
3712     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3713
3714     player->push_delay       = -1;      // initialized when pushing starts
3715     player->push_delay_value = game.initial_push_delay_value;
3716
3717     player->drop_delay = 0;
3718     player->drop_pressed_delay = 0;
3719
3720     player->last_jx = -1;
3721     player->last_jy = -1;
3722     player->jx = -1;
3723     player->jy = -1;
3724
3725     player->shield_normal_time_left = 0;
3726     player->shield_deadly_time_left = 0;
3727
3728     player->last_removed_element = EL_UNDEFINED;
3729
3730     player->inventory_infinite_element = EL_UNDEFINED;
3731     player->inventory_size = 0;
3732
3733     if (level.use_initial_inventory[i])
3734     {
3735       for (j = 0; j < level.initial_inventory_size[i]; j++)
3736       {
3737         int element = level.initial_inventory_content[i][j];
3738         int collect_count = element_info[element].collect_count_initial;
3739         int k;
3740
3741         if (!IS_CUSTOM_ELEMENT(element))
3742           collect_count = 1;
3743
3744         if (collect_count == 0)
3745           player->inventory_infinite_element = element;
3746         else
3747           for (k = 0; k < collect_count; k++)
3748             if (player->inventory_size < MAX_INVENTORY_SIZE)
3749               player->inventory_element[player->inventory_size++] = element;
3750       }
3751     }
3752
3753     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3754     SnapField(player, 0, 0);
3755
3756     map_player_action[i] = i;
3757   }
3758
3759   network_player_action_received = FALSE;
3760
3761   // initial null action
3762   if (network_playing)
3763     SendToServer_MovePlayer(MV_NONE);
3764
3765   FrameCounter = 0;
3766   TimeFrames = 0;
3767   TimePlayed = 0;
3768   TimeLeft = level.time;
3769   TapeTime = 0;
3770
3771   ScreenMovDir = MV_NONE;
3772   ScreenMovPos = 0;
3773   ScreenGfxPos = 0;
3774
3775   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3776
3777   game.robot_wheel_x = -1;
3778   game.robot_wheel_y = -1;
3779
3780   game.exit_x = -1;
3781   game.exit_y = -1;
3782
3783   game.all_players_gone = FALSE;
3784
3785   game.LevelSolved = FALSE;
3786   game.GameOver = FALSE;
3787
3788   game.GamePlayed = !tape.playing;
3789
3790   game.LevelSolved_GameWon = FALSE;
3791   game.LevelSolved_GameEnd = FALSE;
3792   game.LevelSolved_SaveTape = FALSE;
3793   game.LevelSolved_SaveScore = FALSE;
3794
3795   game.LevelSolved_CountingTime = 0;
3796   game.LevelSolved_CountingScore = 0;
3797   game.LevelSolved_CountingHealth = 0;
3798
3799   game.panel.active = TRUE;
3800
3801   game.no_time_limit = (level.time == 0);
3802
3803   game.yamyam_content_nr = 0;
3804   game.robot_wheel_active = FALSE;
3805   game.magic_wall_active = FALSE;
3806   game.magic_wall_time_left = 0;
3807   game.light_time_left = 0;
3808   game.timegate_time_left = 0;
3809   game.switchgate_pos = 0;
3810   game.wind_direction = level.wind_direction_initial;
3811
3812   game.time_final = 0;
3813   game.score_time_final = 0;
3814
3815   game.score = 0;
3816   game.score_final = 0;
3817
3818   game.health = MAX_HEALTH;
3819   game.health_final = MAX_HEALTH;
3820
3821   game.gems_still_needed = level.gems_needed;
3822   game.sokoban_fields_still_needed = 0;
3823   game.sokoban_objects_still_needed = 0;
3824   game.lights_still_needed = 0;
3825   game.players_still_needed = 0;
3826   game.friends_still_needed = 0;
3827
3828   game.lenses_time_left = 0;
3829   game.magnify_time_left = 0;
3830
3831   game.ball_active = level.ball_active_initial;
3832   game.ball_content_nr = 0;
3833
3834   game.explosions_delayed = TRUE;
3835
3836   game.envelope_active = FALSE;
3837
3838   for (i = 0; i < NUM_BELTS; i++)
3839   {
3840     game.belt_dir[i] = MV_NONE;
3841     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3842   }
3843
3844   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3845     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3846
3847 #if DEBUG_INIT_PLAYER
3848   DebugPrintPlayerStatus("Player status at level initialization");
3849 #endif
3850
3851   SCAN_PLAYFIELD(x, y)
3852   {
3853     Tile[x][y] = Last[x][y] = level.field[x][y];
3854     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3855     ChangeDelay[x][y] = 0;
3856     ChangePage[x][y] = -1;
3857     CustomValue[x][y] = 0;              // initialized in InitField()
3858     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3859     AmoebaNr[x][y] = 0;
3860     WasJustMoving[x][y] = 0;
3861     WasJustFalling[x][y] = 0;
3862     CheckCollision[x][y] = 0;
3863     CheckImpact[x][y] = 0;
3864     Stop[x][y] = FALSE;
3865     Pushed[x][y] = FALSE;
3866
3867     ChangeCount[x][y] = 0;
3868     ChangeEvent[x][y] = -1;
3869
3870     ExplodePhase[x][y] = 0;
3871     ExplodeDelay[x][y] = 0;
3872     ExplodeField[x][y] = EX_TYPE_NONE;
3873
3874     RunnerVisit[x][y] = 0;
3875     PlayerVisit[x][y] = 0;
3876
3877     GfxFrame[x][y] = 0;
3878     GfxRandom[x][y] = INIT_GFX_RANDOM();
3879     GfxElement[x][y] = EL_UNDEFINED;
3880     GfxAction[x][y] = ACTION_DEFAULT;
3881     GfxDir[x][y] = MV_NONE;
3882     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3883   }
3884
3885   SCAN_PLAYFIELD(x, y)
3886   {
3887     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3888       emulate_bd = FALSE;
3889     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3890       emulate_sb = FALSE;
3891     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3892       emulate_sp = FALSE;
3893
3894     InitField(x, y, TRUE);
3895
3896     ResetGfxAnimation(x, y);
3897   }
3898
3899   InitBeltMovement();
3900
3901   for (i = 0; i < MAX_PLAYERS; i++)
3902   {
3903     struct PlayerInfo *player = &stored_player[i];
3904
3905     // set number of special actions for bored and sleeping animation
3906     player->num_special_action_bored =
3907       get_num_special_action(player->artwork_element,
3908                              ACTION_BORING_1, ACTION_BORING_LAST);
3909     player->num_special_action_sleeping =
3910       get_num_special_action(player->artwork_element,
3911                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3912   }
3913
3914   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3915                     emulate_sb ? EMU_SOKOBAN :
3916                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3917
3918   // initialize type of slippery elements
3919   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3920   {
3921     if (!IS_CUSTOM_ELEMENT(i))
3922     {
3923       // default: elements slip down either to the left or right randomly
3924       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3925
3926       // SP style elements prefer to slip down on the left side
3927       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3928         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3929
3930       // BD style elements prefer to slip down on the left side
3931       if (game.emulation == EMU_BOULDERDASH)
3932         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3933     }
3934   }
3935
3936   // initialize explosion and ignition delay
3937   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3938   {
3939     if (!IS_CUSTOM_ELEMENT(i))
3940     {
3941       int num_phase = 8;
3942       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3943                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3944                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3945       int last_phase = (num_phase + 1) * delay;
3946       int half_phase = (num_phase / 2) * delay;
3947
3948       element_info[i].explosion_delay = last_phase - 1;
3949       element_info[i].ignition_delay = half_phase;
3950
3951       if (i == EL_BLACK_ORB)
3952         element_info[i].ignition_delay = 1;
3953     }
3954   }
3955
3956   // correct non-moving belts to start moving left
3957   for (i = 0; i < NUM_BELTS; i++)
3958     if (game.belt_dir[i] == MV_NONE)
3959       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3960
3961 #if USE_NEW_PLAYER_ASSIGNMENTS
3962   // use preferred player also in local single-player mode
3963   if (!network.enabled && !game.team_mode)
3964   {
3965     int new_index_nr = setup.network_player_nr;
3966
3967     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3968     {
3969       for (i = 0; i < MAX_PLAYERS; i++)
3970         stored_player[i].connected_locally = FALSE;
3971
3972       stored_player[new_index_nr].connected_locally = TRUE;
3973     }
3974   }
3975
3976   for (i = 0; i < MAX_PLAYERS; i++)
3977   {
3978     stored_player[i].connected = FALSE;
3979
3980     // in network game mode, the local player might not be the first player
3981     if (stored_player[i].connected_locally)
3982       local_player = &stored_player[i];
3983   }
3984
3985   if (!network.enabled)
3986     local_player->connected = TRUE;
3987
3988   if (tape.playing)
3989   {
3990     for (i = 0; i < MAX_PLAYERS; i++)
3991       stored_player[i].connected = tape.player_participates[i];
3992   }
3993   else if (network.enabled)
3994   {
3995     // add team mode players connected over the network (needed for correct
3996     // assignment of player figures from level to locally playing players)
3997
3998     for (i = 0; i < MAX_PLAYERS; i++)
3999       if (stored_player[i].connected_network)
4000         stored_player[i].connected = TRUE;
4001   }
4002   else if (game.team_mode)
4003   {
4004     // try to guess locally connected team mode players (needed for correct
4005     // assignment of player figures from level to locally playing players)
4006
4007     for (i = 0; i < MAX_PLAYERS; i++)
4008       if (setup.input[i].use_joystick ||
4009           setup.input[i].key.left != KSYM_UNDEFINED)
4010         stored_player[i].connected = TRUE;
4011   }
4012
4013 #if DEBUG_INIT_PLAYER
4014   DebugPrintPlayerStatus("Player status after level initialization");
4015 #endif
4016
4017 #if DEBUG_INIT_PLAYER
4018   Debug("game:init:player", "Reassigning players ...");
4019 #endif
4020
4021   // check if any connected player was not found in playfield
4022   for (i = 0; i < MAX_PLAYERS; i++)
4023   {
4024     struct PlayerInfo *player = &stored_player[i];
4025
4026     if (player->connected && !player->present)
4027     {
4028       struct PlayerInfo *field_player = NULL;
4029
4030 #if DEBUG_INIT_PLAYER
4031       Debug("game:init:player",
4032             "- looking for field player for player %d ...", i + 1);
4033 #endif
4034
4035       // assign first free player found that is present in the playfield
4036
4037       // first try: look for unmapped playfield player that is not connected
4038       for (j = 0; j < MAX_PLAYERS; j++)
4039         if (field_player == NULL &&
4040             stored_player[j].present &&
4041             !stored_player[j].mapped &&
4042             !stored_player[j].connected)
4043           field_player = &stored_player[j];
4044
4045       // second try: look for *any* unmapped playfield player
4046       for (j = 0; j < MAX_PLAYERS; j++)
4047         if (field_player == NULL &&
4048             stored_player[j].present &&
4049             !stored_player[j].mapped)
4050           field_player = &stored_player[j];
4051
4052       if (field_player != NULL)
4053       {
4054         int jx = field_player->jx, jy = field_player->jy;
4055
4056 #if DEBUG_INIT_PLAYER
4057         Debug("game:init:player", "- found player %d",
4058               field_player->index_nr + 1);
4059 #endif
4060
4061         player->present = FALSE;
4062         player->active = FALSE;
4063
4064         field_player->present = TRUE;
4065         field_player->active = TRUE;
4066
4067         /*
4068         player->initial_element = field_player->initial_element;
4069         player->artwork_element = field_player->artwork_element;
4070
4071         player->block_last_field       = field_player->block_last_field;
4072         player->block_delay_adjustment = field_player->block_delay_adjustment;
4073         */
4074
4075         StorePlayer[jx][jy] = field_player->element_nr;
4076
4077         field_player->jx = field_player->last_jx = jx;
4078         field_player->jy = field_player->last_jy = jy;
4079
4080         if (local_player == player)
4081           local_player = field_player;
4082
4083         map_player_action[field_player->index_nr] = i;
4084
4085         field_player->mapped = TRUE;
4086
4087 #if DEBUG_INIT_PLAYER
4088         Debug("game:init:player", "- map_player_action[%d] == %d",
4089               field_player->index_nr + 1, i + 1);
4090 #endif
4091       }
4092     }
4093
4094     if (player->connected && player->present)
4095       player->mapped = TRUE;
4096   }
4097
4098 #if DEBUG_INIT_PLAYER
4099   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4100 #endif
4101
4102 #else
4103
4104   // check if any connected player was not found in playfield
4105   for (i = 0; i < MAX_PLAYERS; i++)
4106   {
4107     struct PlayerInfo *player = &stored_player[i];
4108
4109     if (player->connected && !player->present)
4110     {
4111       for (j = 0; j < MAX_PLAYERS; j++)
4112       {
4113         struct PlayerInfo *field_player = &stored_player[j];
4114         int jx = field_player->jx, jy = field_player->jy;
4115
4116         // assign first free player found that is present in the playfield
4117         if (field_player->present && !field_player->connected)
4118         {
4119           player->present = TRUE;
4120           player->active = TRUE;
4121
4122           field_player->present = FALSE;
4123           field_player->active = FALSE;
4124
4125           player->initial_element = field_player->initial_element;
4126           player->artwork_element = field_player->artwork_element;
4127
4128           player->block_last_field       = field_player->block_last_field;
4129           player->block_delay_adjustment = field_player->block_delay_adjustment;
4130
4131           StorePlayer[jx][jy] = player->element_nr;
4132
4133           player->jx = player->last_jx = jx;
4134           player->jy = player->last_jy = jy;
4135
4136           break;
4137         }
4138       }
4139     }
4140   }
4141 #endif
4142
4143 #if 0
4144   Debug("game:init:player", "local_player->present == %d",
4145         local_player->present);
4146 #endif
4147
4148   // set focus to local player for network games, else to all players
4149   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4150   game.centered_player_nr_next = game.centered_player_nr;
4151   game.set_centered_player = FALSE;
4152   game.set_centered_player_wrap = FALSE;
4153
4154   if (network_playing && tape.recording)
4155   {
4156     // store client dependent player focus when recording network games
4157     tape.centered_player_nr_next = game.centered_player_nr_next;
4158     tape.set_centered_player = TRUE;
4159   }
4160
4161   if (tape.playing)
4162   {
4163     // when playing a tape, eliminate all players who do not participate
4164
4165 #if USE_NEW_PLAYER_ASSIGNMENTS
4166
4167     if (!game.team_mode)
4168     {
4169       for (i = 0; i < MAX_PLAYERS; i++)
4170       {
4171         if (stored_player[i].active &&
4172             !tape.player_participates[map_player_action[i]])
4173         {
4174           struct PlayerInfo *player = &stored_player[i];
4175           int jx = player->jx, jy = player->jy;
4176
4177 #if DEBUG_INIT_PLAYER
4178           Debug("game:init:player", "Removing player %d at (%d, %d)",
4179                 i + 1, jx, jy);
4180 #endif
4181
4182           player->active = FALSE;
4183           StorePlayer[jx][jy] = 0;
4184           Tile[jx][jy] = EL_EMPTY;
4185         }
4186       }
4187     }
4188
4189 #else
4190
4191     for (i = 0; i < MAX_PLAYERS; i++)
4192     {
4193       if (stored_player[i].active &&
4194           !tape.player_participates[i])
4195       {
4196         struct PlayerInfo *player = &stored_player[i];
4197         int jx = player->jx, jy = player->jy;
4198
4199         player->active = FALSE;
4200         StorePlayer[jx][jy] = 0;
4201         Tile[jx][jy] = EL_EMPTY;
4202       }
4203     }
4204 #endif
4205   }
4206   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4207   {
4208     // when in single player mode, eliminate all but the local player
4209
4210     for (i = 0; i < MAX_PLAYERS; i++)
4211     {
4212       struct PlayerInfo *player = &stored_player[i];
4213
4214       if (player->active && player != local_player)
4215       {
4216         int jx = player->jx, jy = player->jy;
4217
4218         player->active = FALSE;
4219         player->present = FALSE;
4220
4221         StorePlayer[jx][jy] = 0;
4222         Tile[jx][jy] = EL_EMPTY;
4223       }
4224     }
4225   }
4226
4227   for (i = 0; i < MAX_PLAYERS; i++)
4228     if (stored_player[i].active)
4229       game.players_still_needed++;
4230
4231   if (level.solved_by_one_player)
4232     game.players_still_needed = 1;
4233
4234   // when recording the game, store which players take part in the game
4235   if (tape.recording)
4236   {
4237 #if USE_NEW_PLAYER_ASSIGNMENTS
4238     for (i = 0; i < MAX_PLAYERS; i++)
4239       if (stored_player[i].connected)
4240         tape.player_participates[i] = TRUE;
4241 #else
4242     for (i = 0; i < MAX_PLAYERS; i++)
4243       if (stored_player[i].active)
4244         tape.player_participates[i] = TRUE;
4245 #endif
4246   }
4247
4248 #if DEBUG_INIT_PLAYER
4249   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4250 #endif
4251
4252   if (BorderElement == EL_EMPTY)
4253   {
4254     SBX_Left = 0;
4255     SBX_Right = lev_fieldx - SCR_FIELDX;
4256     SBY_Upper = 0;
4257     SBY_Lower = lev_fieldy - SCR_FIELDY;
4258   }
4259   else
4260   {
4261     SBX_Left = -1;
4262     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4263     SBY_Upper = -1;
4264     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4265   }
4266
4267   if (full_lev_fieldx <= SCR_FIELDX)
4268     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4269   if (full_lev_fieldy <= SCR_FIELDY)
4270     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4271
4272   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4273     SBX_Left--;
4274   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4275     SBY_Upper--;
4276
4277   // if local player not found, look for custom element that might create
4278   // the player (make some assumptions about the right custom element)
4279   if (!local_player->present)
4280   {
4281     int start_x = 0, start_y = 0;
4282     int found_rating = 0;
4283     int found_element = EL_UNDEFINED;
4284     int player_nr = local_player->index_nr;
4285
4286     SCAN_PLAYFIELD(x, y)
4287     {
4288       int element = Tile[x][y];
4289       int content;
4290       int xx, yy;
4291       boolean is_player;
4292
4293       if (level.use_start_element[player_nr] &&
4294           level.start_element[player_nr] == element &&
4295           found_rating < 4)
4296       {
4297         start_x = x;
4298         start_y = y;
4299
4300         found_rating = 4;
4301         found_element = element;
4302       }
4303
4304       if (!IS_CUSTOM_ELEMENT(element))
4305         continue;
4306
4307       if (CAN_CHANGE(element))
4308       {
4309         for (i = 0; i < element_info[element].num_change_pages; i++)
4310         {
4311           // check for player created from custom element as single target
4312           content = element_info[element].change_page[i].target_element;
4313           is_player = ELEM_IS_PLAYER(content);
4314
4315           if (is_player && (found_rating < 3 ||
4316                             (found_rating == 3 && element < found_element)))
4317           {
4318             start_x = x;
4319             start_y = y;
4320
4321             found_rating = 3;
4322             found_element = element;
4323           }
4324         }
4325       }
4326
4327       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4328       {
4329         // check for player created from custom element as explosion content
4330         content = element_info[element].content.e[xx][yy];
4331         is_player = ELEM_IS_PLAYER(content);
4332
4333         if (is_player && (found_rating < 2 ||
4334                           (found_rating == 2 && element < found_element)))
4335         {
4336           start_x = x + xx - 1;
4337           start_y = y + yy - 1;
4338
4339           found_rating = 2;
4340           found_element = element;
4341         }
4342
4343         if (!CAN_CHANGE(element))
4344           continue;
4345
4346         for (i = 0; i < element_info[element].num_change_pages; i++)
4347         {
4348           // check for player created from custom element as extended target
4349           content =
4350             element_info[element].change_page[i].target_content.e[xx][yy];
4351
4352           is_player = ELEM_IS_PLAYER(content);
4353
4354           if (is_player && (found_rating < 1 ||
4355                             (found_rating == 1 && element < found_element)))
4356           {
4357             start_x = x + xx - 1;
4358             start_y = y + yy - 1;
4359
4360             found_rating = 1;
4361             found_element = element;
4362           }
4363         }
4364       }
4365     }
4366
4367     scroll_x = SCROLL_POSITION_X(start_x);
4368     scroll_y = SCROLL_POSITION_Y(start_y);
4369   }
4370   else
4371   {
4372     scroll_x = SCROLL_POSITION_X(local_player->jx);
4373     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4374   }
4375
4376   // !!! FIX THIS (START) !!!
4377   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4378   {
4379     InitGameEngine_EM();
4380   }
4381   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4382   {
4383     InitGameEngine_SP();
4384   }
4385   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4386   {
4387     InitGameEngine_MM();
4388   }
4389   else
4390   {
4391     DrawLevel(REDRAW_FIELD);
4392     DrawAllPlayers();
4393
4394     // after drawing the level, correct some elements
4395     if (game.timegate_time_left == 0)
4396       CloseAllOpenTimegates();
4397   }
4398
4399   // blit playfield from scroll buffer to normal back buffer for fading in
4400   BlitScreenToBitmap(backbuffer);
4401   // !!! FIX THIS (END) !!!
4402
4403   DrawMaskedBorder(fade_mask);
4404
4405   FadeIn(fade_mask);
4406
4407 #if 1
4408   // full screen redraw is required at this point in the following cases:
4409   // - special editor door undrawn when game was started from level editor
4410   // - drawing area (playfield) was changed and has to be removed completely
4411   redraw_mask = REDRAW_ALL;
4412   BackToFront();
4413 #endif
4414
4415   if (!game.restart_level)
4416   {
4417     // copy default game door content to main double buffer
4418
4419     // !!! CHECK AGAIN !!!
4420     SetPanelBackground();
4421     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4422     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4423   }
4424
4425   SetPanelBackground();
4426   SetDrawBackgroundMask(REDRAW_DOOR_1);
4427
4428   UpdateAndDisplayGameControlValues();
4429
4430   if (!game.restart_level)
4431   {
4432     UnmapGameButtons();
4433     UnmapTapeButtons();
4434
4435     FreeGameButtons();
4436     CreateGameButtons();
4437
4438     MapGameButtons();
4439     MapTapeButtons();
4440
4441     // copy actual game door content to door double buffer for OpenDoor()
4442     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4443
4444     OpenDoor(DOOR_OPEN_ALL);
4445
4446     KeyboardAutoRepeatOffUnlessAutoplay();
4447
4448 #if DEBUG_INIT_PLAYER
4449     DebugPrintPlayerStatus("Player status (final)");
4450 #endif
4451   }
4452
4453   UnmapAllGadgets();
4454
4455   MapGameButtons();
4456   MapTapeButtons();
4457
4458   if (!game.restart_level && !tape.playing)
4459   {
4460     LevelStats_incPlayed(level_nr);
4461
4462     SaveLevelSetup_SeriesInfo();
4463   }
4464
4465   game.restart_level = FALSE;
4466   game.restart_game_message = NULL;
4467
4468   game.request_active = FALSE;
4469   game.request_active_or_moving = FALSE;
4470
4471   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4472     InitGameActions_MM();
4473
4474   SaveEngineSnapshotToListInitial();
4475
4476   if (!game.restart_level)
4477   {
4478     PlaySound(SND_GAME_STARTING);
4479
4480     if (setup.sound_music)
4481       PlayLevelMusic();
4482   }
4483 }
4484
4485 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4486                         int actual_player_x, int actual_player_y)
4487 {
4488   // this is used for non-R'n'D game engines to update certain engine values
4489
4490   // needed to determine if sounds are played within the visible screen area
4491   scroll_x = actual_scroll_x;
4492   scroll_y = actual_scroll_y;
4493
4494   // needed to get player position for "follow finger" playing input method
4495   local_player->jx = actual_player_x;
4496   local_player->jy = actual_player_y;
4497 }
4498
4499 void InitMovDir(int x, int y)
4500 {
4501   int i, element = Tile[x][y];
4502   static int xy[4][2] =
4503   {
4504     {  0, +1 },
4505     { +1,  0 },
4506     {  0, -1 },
4507     { -1,  0 }
4508   };
4509   static int direction[3][4] =
4510   {
4511     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4512     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4513     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4514   };
4515
4516   switch (element)
4517   {
4518     case EL_BUG_RIGHT:
4519     case EL_BUG_UP:
4520     case EL_BUG_LEFT:
4521     case EL_BUG_DOWN:
4522       Tile[x][y] = EL_BUG;
4523       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4524       break;
4525
4526     case EL_SPACESHIP_RIGHT:
4527     case EL_SPACESHIP_UP:
4528     case EL_SPACESHIP_LEFT:
4529     case EL_SPACESHIP_DOWN:
4530       Tile[x][y] = EL_SPACESHIP;
4531       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4532       break;
4533
4534     case EL_BD_BUTTERFLY_RIGHT:
4535     case EL_BD_BUTTERFLY_UP:
4536     case EL_BD_BUTTERFLY_LEFT:
4537     case EL_BD_BUTTERFLY_DOWN:
4538       Tile[x][y] = EL_BD_BUTTERFLY;
4539       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4540       break;
4541
4542     case EL_BD_FIREFLY_RIGHT:
4543     case EL_BD_FIREFLY_UP:
4544     case EL_BD_FIREFLY_LEFT:
4545     case EL_BD_FIREFLY_DOWN:
4546       Tile[x][y] = EL_BD_FIREFLY;
4547       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4548       break;
4549
4550     case EL_PACMAN_RIGHT:
4551     case EL_PACMAN_UP:
4552     case EL_PACMAN_LEFT:
4553     case EL_PACMAN_DOWN:
4554       Tile[x][y] = EL_PACMAN;
4555       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4556       break;
4557
4558     case EL_YAMYAM_LEFT:
4559     case EL_YAMYAM_RIGHT:
4560     case EL_YAMYAM_UP:
4561     case EL_YAMYAM_DOWN:
4562       Tile[x][y] = EL_YAMYAM;
4563       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4564       break;
4565
4566     case EL_SP_SNIKSNAK:
4567       MovDir[x][y] = MV_UP;
4568       break;
4569
4570     case EL_SP_ELECTRON:
4571       MovDir[x][y] = MV_LEFT;
4572       break;
4573
4574     case EL_MOLE_LEFT:
4575     case EL_MOLE_RIGHT:
4576     case EL_MOLE_UP:
4577     case EL_MOLE_DOWN:
4578       Tile[x][y] = EL_MOLE;
4579       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4580       break;
4581
4582     case EL_SPRING_LEFT:
4583     case EL_SPRING_RIGHT:
4584       Tile[x][y] = EL_SPRING;
4585       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4586       break;
4587
4588     default:
4589       if (IS_CUSTOM_ELEMENT(element))
4590       {
4591         struct ElementInfo *ei = &element_info[element];
4592         int move_direction_initial = ei->move_direction_initial;
4593         int move_pattern = ei->move_pattern;
4594
4595         if (move_direction_initial == MV_START_PREVIOUS)
4596         {
4597           if (MovDir[x][y] != MV_NONE)
4598             return;
4599
4600           move_direction_initial = MV_START_AUTOMATIC;
4601         }
4602
4603         if (move_direction_initial == MV_START_RANDOM)
4604           MovDir[x][y] = 1 << RND(4);
4605         else if (move_direction_initial & MV_ANY_DIRECTION)
4606           MovDir[x][y] = move_direction_initial;
4607         else if (move_pattern == MV_ALL_DIRECTIONS ||
4608                  move_pattern == MV_TURNING_LEFT ||
4609                  move_pattern == MV_TURNING_RIGHT ||
4610                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4611                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4612                  move_pattern == MV_TURNING_RANDOM)
4613           MovDir[x][y] = 1 << RND(4);
4614         else if (move_pattern == MV_HORIZONTAL)
4615           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4616         else if (move_pattern == MV_VERTICAL)
4617           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4618         else if (move_pattern & MV_ANY_DIRECTION)
4619           MovDir[x][y] = element_info[element].move_pattern;
4620         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4621                  move_pattern == MV_ALONG_RIGHT_SIDE)
4622         {
4623           // use random direction as default start direction
4624           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4625             MovDir[x][y] = 1 << RND(4);
4626
4627           for (i = 0; i < NUM_DIRECTIONS; i++)
4628           {
4629             int x1 = x + xy[i][0];
4630             int y1 = y + xy[i][1];
4631
4632             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4633             {
4634               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4635                 MovDir[x][y] = direction[0][i];
4636               else
4637                 MovDir[x][y] = direction[1][i];
4638
4639               break;
4640             }
4641           }
4642         }                
4643       }
4644       else
4645       {
4646         MovDir[x][y] = 1 << RND(4);
4647
4648         if (element != EL_BUG &&
4649             element != EL_SPACESHIP &&
4650             element != EL_BD_BUTTERFLY &&
4651             element != EL_BD_FIREFLY)
4652           break;
4653
4654         for (i = 0; i < NUM_DIRECTIONS; i++)
4655         {
4656           int x1 = x + xy[i][0];
4657           int y1 = y + xy[i][1];
4658
4659           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4660           {
4661             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4662             {
4663               MovDir[x][y] = direction[0][i];
4664               break;
4665             }
4666             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4667                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4668             {
4669               MovDir[x][y] = direction[1][i];
4670               break;
4671             }
4672           }
4673         }
4674       }
4675       break;
4676   }
4677
4678   GfxDir[x][y] = MovDir[x][y];
4679 }
4680
4681 void InitAmoebaNr(int x, int y)
4682 {
4683   int i;
4684   int group_nr = AmoebaNeighbourNr(x, y);
4685
4686   if (group_nr == 0)
4687   {
4688     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4689     {
4690       if (AmoebaCnt[i] == 0)
4691       {
4692         group_nr = i;
4693         break;
4694       }
4695     }
4696   }
4697
4698   AmoebaNr[x][y] = group_nr;
4699   AmoebaCnt[group_nr]++;
4700   AmoebaCnt2[group_nr]++;
4701 }
4702
4703 static void LevelSolved(void)
4704 {
4705   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4706       game.players_still_needed > 0)
4707     return;
4708
4709   game.LevelSolved = TRUE;
4710   game.GameOver = TRUE;
4711 }
4712
4713 void GameWon(void)
4714 {
4715   static int time_count_steps;
4716   static int time, time_final;
4717   static float score, score_final; // needed for time score < 10 for 10 seconds
4718   static int health, health_final;
4719   static int game_over_delay_1 = 0;
4720   static int game_over_delay_2 = 0;
4721   static int game_over_delay_3 = 0;
4722   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4723   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4724
4725   if (!game.LevelSolved_GameWon)
4726   {
4727     int i;
4728
4729     // do not start end game actions before the player stops moving (to exit)
4730     if (local_player->active && local_player->MovPos)
4731       return;
4732
4733     game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4734     game.score_time_final = (level.use_step_counter ? TimePlayed :
4735                              TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4736
4737     game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4738                         game_em.lev->score :
4739                         level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4740                         game_mm.score :
4741                         game.score);
4742
4743     game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4744                          MM_HEALTH(game_mm.laser_overload_value) :
4745                          game.health);
4746
4747     game.LevelSolved_CountingTime = game.time_final;
4748     game.LevelSolved_CountingScore = game.score_final;
4749     game.LevelSolved_CountingHealth = game.health_final;
4750
4751     game.LevelSolved_GameWon = TRUE;
4752     game.LevelSolved_SaveTape = tape.recording;
4753     game.LevelSolved_SaveScore = !tape.playing;
4754
4755     if (!tape.playing)
4756     {
4757       LevelStats_incSolved(level_nr);
4758
4759       SaveLevelSetup_SeriesInfo();
4760     }
4761
4762     if (tape.auto_play)         // tape might already be stopped here
4763       tape.auto_play_level_solved = TRUE;
4764
4765     TapeStop();
4766
4767     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4768     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4769     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4770
4771     time = time_final = game.time_final;
4772     score = score_final = game.score_final;
4773     health = health_final = game.health_final;
4774
4775     if (time_score > 0)
4776     {
4777       int time_final_max = 999;
4778       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4779       int time_frames = 0;
4780       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4781       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4782
4783       if (TimeLeft > 0)
4784       {
4785         time_final = 0;
4786         time_frames = time_frames_left;
4787       }
4788       else if (game.no_time_limit && TimePlayed < time_final_max)
4789       {
4790         time_final = time_final_max;
4791         time_frames = time_frames_final_max - time_frames_played;
4792       }
4793
4794       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4795
4796       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4797
4798       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4799       {
4800         health_final = 0;
4801         score_final += health * time_score;
4802       }
4803
4804       game.score_final = score_final;
4805       game.health_final = health_final;
4806     }
4807
4808     if (level_editor_test_game || !setup.count_score_after_game)
4809     {
4810       time = time_final;
4811       score = score_final;
4812
4813       game.LevelSolved_CountingTime = time;
4814       game.LevelSolved_CountingScore = score;
4815
4816       game_panel_controls[GAME_PANEL_TIME].value = time;
4817       game_panel_controls[GAME_PANEL_SCORE].value = score;
4818
4819       DisplayGameControlValues();
4820     }
4821
4822     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4823     {
4824       // check if last player has left the level
4825       if (game.exit_x >= 0 &&
4826           game.exit_y >= 0)
4827       {
4828         int x = game.exit_x;
4829         int y = game.exit_y;
4830         int element = Tile[x][y];
4831
4832         // close exit door after last player
4833         if ((game.all_players_gone &&
4834              (element == EL_EXIT_OPEN ||
4835               element == EL_SP_EXIT_OPEN ||
4836               element == EL_STEEL_EXIT_OPEN)) ||
4837             element == EL_EM_EXIT_OPEN ||
4838             element == EL_EM_STEEL_EXIT_OPEN)
4839         {
4840
4841           Tile[x][y] =
4842             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4843              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4844              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4845              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4846              EL_EM_STEEL_EXIT_CLOSING);
4847
4848           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4849         }
4850
4851         // player disappears
4852         DrawLevelField(x, y);
4853       }
4854
4855       for (i = 0; i < MAX_PLAYERS; i++)
4856       {
4857         struct PlayerInfo *player = &stored_player[i];
4858
4859         if (player->present)
4860         {
4861           RemovePlayer(player);
4862
4863           // player disappears
4864           DrawLevelField(player->jx, player->jy);
4865         }
4866       }
4867     }
4868
4869     PlaySound(SND_GAME_WINNING);
4870   }
4871
4872   if (setup.count_score_after_game)
4873   {
4874     if (time != time_final)
4875     {
4876       if (game_over_delay_1 > 0)
4877       {
4878         game_over_delay_1--;
4879
4880         return;
4881       }
4882
4883       int time_to_go = ABS(time_final - time);
4884       int time_count_dir = (time < time_final ? +1 : -1);
4885
4886       if (time_to_go < time_count_steps)
4887         time_count_steps = 1;
4888
4889       time  += time_count_steps * time_count_dir;
4890       score += time_count_steps * time_score;
4891
4892       // set final score to correct rounding differences after counting score
4893       if (time == time_final)
4894         score = score_final;
4895
4896       game.LevelSolved_CountingTime = time;
4897       game.LevelSolved_CountingScore = score;
4898
4899       game_panel_controls[GAME_PANEL_TIME].value = time;
4900       game_panel_controls[GAME_PANEL_SCORE].value = score;
4901
4902       DisplayGameControlValues();
4903
4904       if (time == time_final)
4905         StopSound(SND_GAME_LEVELTIME_BONUS);
4906       else if (setup.sound_loops)
4907         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4908       else
4909         PlaySound(SND_GAME_LEVELTIME_BONUS);
4910
4911       return;
4912     }
4913
4914     if (health != health_final)
4915     {
4916       if (game_over_delay_2 > 0)
4917       {
4918         game_over_delay_2--;
4919
4920         return;
4921       }
4922
4923       int health_count_dir = (health < health_final ? +1 : -1);
4924
4925       health += health_count_dir;
4926       score  += time_score;
4927
4928       game.LevelSolved_CountingHealth = health;
4929       game.LevelSolved_CountingScore = score;
4930
4931       game_panel_controls[GAME_PANEL_HEALTH].value = health;
4932       game_panel_controls[GAME_PANEL_SCORE].value = score;
4933
4934       DisplayGameControlValues();
4935
4936       if (health == health_final)
4937         StopSound(SND_GAME_LEVELTIME_BONUS);
4938       else if (setup.sound_loops)
4939         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4940       else
4941         PlaySound(SND_GAME_LEVELTIME_BONUS);
4942
4943       return;
4944     }
4945   }
4946
4947   game.panel.active = FALSE;
4948
4949   if (game_over_delay_3 > 0)
4950   {
4951     game_over_delay_3--;
4952
4953     return;
4954   }
4955
4956   GameEnd();
4957 }
4958
4959 void GameEnd(void)
4960 {
4961   // used instead of "level_nr" (needed for network games)
4962   int last_level_nr = levelset.level_nr;
4963   int highlight_position;
4964
4965   game.LevelSolved_GameEnd = TRUE;
4966
4967   if (game.LevelSolved_SaveTape)
4968   {
4969     // make sure that request dialog to save tape does not open door again
4970     if (!global.use_envelope_request)
4971       CloseDoor(DOOR_CLOSE_1);
4972
4973     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4974
4975     // set unique basename for score tape (also saved in high score table)
4976     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
4977   }
4978
4979   // if no tape is to be saved, close both doors simultaneously
4980   CloseDoor(DOOR_CLOSE_ALL);
4981
4982   if (level_editor_test_game)
4983   {
4984     SetGameStatus(GAME_MODE_MAIN);
4985
4986     DrawMainMenu();
4987
4988     return;
4989   }
4990
4991   if (!game.LevelSolved_SaveScore)
4992   {
4993     SetGameStatus(GAME_MODE_MAIN);
4994
4995     DrawMainMenu();
4996
4997     return;
4998   }
4999
5000   if (level_nr == leveldir_current->handicap_level)
5001   {
5002     leveldir_current->handicap_level++;
5003
5004     SaveLevelSetup_SeriesInfo();
5005   }
5006
5007   if (setup.increment_levels &&
5008       level_nr < leveldir_current->last_level &&
5009       !network_playing)
5010   {
5011     level_nr++;         // advance to next level
5012     TapeErase();        // start with empty tape
5013
5014     if (setup.auto_play_next_level)
5015     {
5016       LoadLevel(level_nr);
5017
5018       SaveLevelSetup_SeriesInfo();
5019     }
5020   }
5021
5022   highlight_position = NewHighScore(last_level_nr);
5023
5024   if (highlight_position >= 0 && setup.show_scores_after_game)
5025   {
5026     SetGameStatus(GAME_MODE_SCORES);
5027
5028     DrawHallOfFame(last_level_nr, highlight_position);
5029   }
5030   else if (setup.auto_play_next_level && setup.increment_levels &&
5031            last_level_nr < leveldir_current->last_level &&
5032            !network_playing)
5033   {
5034     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5035   }
5036   else
5037   {
5038     SetGameStatus(GAME_MODE_MAIN);
5039
5040     DrawMainMenu();
5041   }
5042 }
5043
5044 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry)
5045 {
5046   boolean one_score_entry_per_name = !program.many_scores_per_name;
5047   int i;
5048
5049   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME))
5050     return -1;
5051
5052   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5053   {
5054     struct ScoreEntry *entry = &list->entry[i];
5055     boolean score_is_better = (new_entry->score >  entry->score);
5056     boolean score_is_equal  = (new_entry->score == entry->score);
5057     boolean time_is_better  = (new_entry->time  <  entry->time);
5058     boolean time_is_equal   = (new_entry->time  == entry->time);
5059     boolean better_by_score = (score_is_better ||
5060                                (score_is_equal && time_is_better));
5061     boolean better_by_time  = (time_is_better ||
5062                                (time_is_equal && score_is_better));
5063     boolean is_better = (level.rate_time_over_score ? better_by_time :
5064                          better_by_score);
5065     boolean entry_is_empty = (entry->score == 0 &&
5066                               entry->time == 0);
5067
5068     if (is_better || entry_is_empty)
5069     {
5070       // player has made it to the hall of fame
5071
5072       if (i < MAX_SCORE_ENTRIES - 1)
5073       {
5074         int m = MAX_SCORE_ENTRIES - 1;
5075         int l;
5076
5077         if (one_score_entry_per_name)
5078         {
5079           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5080             if (strEqual(list->entry[l].name, setup.player_name))
5081               m = l;
5082
5083           if (m == i)   // player's new highscore overwrites his old one
5084             goto put_into_list;
5085         }
5086
5087         for (l = m; l > i; l--)
5088           list->entry[l] = list->entry[l - 1];
5089       }
5090
5091       put_into_list:
5092
5093       *entry = *new_entry;
5094
5095       return i;
5096     }
5097     else if (one_score_entry_per_name &&
5098              strEqual(entry->name, setup.player_name))
5099     {
5100       // player already in high score list with better score or time
5101
5102       return -1;
5103     }
5104   }
5105
5106   return -1;
5107 }
5108
5109 int NewHighScore(int level_nr)
5110 {
5111   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5112
5113   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5114   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5115
5116   new_entry.score = game.score_final;
5117   new_entry.time = game.score_time_final;
5118
5119   LoadScore(level_nr);
5120
5121   int position = addScoreEntry(&scores, &new_entry);
5122
5123   if (position >= 0)
5124   {
5125     SaveScoreTape(level_nr);
5126     SaveScore(level_nr);
5127   }
5128
5129   return position;
5130 }
5131
5132 static int getElementMoveStepsizeExt(int x, int y, int direction)
5133 {
5134   int element = Tile[x][y];
5135   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5136   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5137   int horiz_move = (dx != 0);
5138   int sign = (horiz_move ? dx : dy);
5139   int step = sign * element_info[element].move_stepsize;
5140
5141   // special values for move stepsize for spring and things on conveyor belt
5142   if (horiz_move)
5143   {
5144     if (CAN_FALL(element) &&
5145         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5146       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5147     else if (element == EL_SPRING)
5148       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5149   }
5150
5151   return step;
5152 }
5153
5154 static int getElementMoveStepsize(int x, int y)
5155 {
5156   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5157 }
5158
5159 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5160 {
5161   if (player->GfxAction != action || player->GfxDir != dir)
5162   {
5163     player->GfxAction = action;
5164     player->GfxDir = dir;
5165     player->Frame = 0;
5166     player->StepFrame = 0;
5167   }
5168 }
5169
5170 static void ResetGfxFrame(int x, int y)
5171 {
5172   // profiling showed that "autotest" spends 10~20% of its time in this function
5173   if (DrawingDeactivatedField())
5174     return;
5175
5176   int element = Tile[x][y];
5177   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5178
5179   if (graphic_info[graphic].anim_global_sync)
5180     GfxFrame[x][y] = FrameCounter;
5181   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5182     GfxFrame[x][y] = CustomValue[x][y];
5183   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5184     GfxFrame[x][y] = element_info[element].collect_score;
5185   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5186     GfxFrame[x][y] = ChangeDelay[x][y];
5187 }
5188
5189 static void ResetGfxAnimation(int x, int y)
5190 {
5191   GfxAction[x][y] = ACTION_DEFAULT;
5192   GfxDir[x][y] = MovDir[x][y];
5193   GfxFrame[x][y] = 0;
5194
5195   ResetGfxFrame(x, y);
5196 }
5197
5198 static void ResetRandomAnimationValue(int x, int y)
5199 {
5200   GfxRandom[x][y] = INIT_GFX_RANDOM();
5201 }
5202
5203 static void InitMovingField(int x, int y, int direction)
5204 {
5205   int element = Tile[x][y];
5206   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5207   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5208   int newx = x + dx;
5209   int newy = y + dy;
5210   boolean is_moving_before, is_moving_after;
5211
5212   // check if element was/is moving or being moved before/after mode change
5213   is_moving_before = (WasJustMoving[x][y] != 0);
5214   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5215
5216   // reset animation only for moving elements which change direction of moving
5217   // or which just started or stopped moving
5218   // (else CEs with property "can move" / "not moving" are reset each frame)
5219   if (is_moving_before != is_moving_after ||
5220       direction != MovDir[x][y])
5221     ResetGfxAnimation(x, y);
5222
5223   MovDir[x][y] = direction;
5224   GfxDir[x][y] = direction;
5225
5226   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5227                      direction == MV_DOWN && CAN_FALL(element) ?
5228                      ACTION_FALLING : ACTION_MOVING);
5229
5230   // this is needed for CEs with property "can move" / "not moving"
5231
5232   if (is_moving_after)
5233   {
5234     if (Tile[newx][newy] == EL_EMPTY)
5235       Tile[newx][newy] = EL_BLOCKED;
5236
5237     MovDir[newx][newy] = MovDir[x][y];
5238
5239     CustomValue[newx][newy] = CustomValue[x][y];
5240
5241     GfxFrame[newx][newy] = GfxFrame[x][y];
5242     GfxRandom[newx][newy] = GfxRandom[x][y];
5243     GfxAction[newx][newy] = GfxAction[x][y];
5244     GfxDir[newx][newy] = GfxDir[x][y];
5245   }
5246 }
5247
5248 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5249 {
5250   int direction = MovDir[x][y];
5251   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5252   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5253
5254   *goes_to_x = newx;
5255   *goes_to_y = newy;
5256 }
5257
5258 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5259 {
5260   int oldx = x, oldy = y;
5261   int direction = MovDir[x][y];
5262
5263   if (direction == MV_LEFT)
5264     oldx++;
5265   else if (direction == MV_RIGHT)
5266     oldx--;
5267   else if (direction == MV_UP)
5268     oldy++;
5269   else if (direction == MV_DOWN)
5270     oldy--;
5271
5272   *comes_from_x = oldx;
5273   *comes_from_y = oldy;
5274 }
5275
5276 static int MovingOrBlocked2Element(int x, int y)
5277 {
5278   int element = Tile[x][y];
5279
5280   if (element == EL_BLOCKED)
5281   {
5282     int oldx, oldy;
5283
5284     Blocked2Moving(x, y, &oldx, &oldy);
5285     return Tile[oldx][oldy];
5286   }
5287   else
5288     return element;
5289 }
5290
5291 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5292 {
5293   // like MovingOrBlocked2Element(), but if element is moving
5294   // and (x,y) is the field the moving element is just leaving,
5295   // return EL_BLOCKED instead of the element value
5296   int element = Tile[x][y];
5297
5298   if (IS_MOVING(x, y))
5299   {
5300     if (element == EL_BLOCKED)
5301     {
5302       int oldx, oldy;
5303
5304       Blocked2Moving(x, y, &oldx, &oldy);
5305       return Tile[oldx][oldy];
5306     }
5307     else
5308       return EL_BLOCKED;
5309   }
5310   else
5311     return element;
5312 }
5313
5314 static void RemoveField(int x, int y)
5315 {
5316   Tile[x][y] = EL_EMPTY;
5317
5318   MovPos[x][y] = 0;
5319   MovDir[x][y] = 0;
5320   MovDelay[x][y] = 0;
5321
5322   CustomValue[x][y] = 0;
5323
5324   AmoebaNr[x][y] = 0;
5325   ChangeDelay[x][y] = 0;
5326   ChangePage[x][y] = -1;
5327   Pushed[x][y] = FALSE;
5328
5329   GfxElement[x][y] = EL_UNDEFINED;
5330   GfxAction[x][y] = ACTION_DEFAULT;
5331   GfxDir[x][y] = MV_NONE;
5332 }
5333
5334 static void RemoveMovingField(int x, int y)
5335 {
5336   int oldx = x, oldy = y, newx = x, newy = y;
5337   int element = Tile[x][y];
5338   int next_element = EL_UNDEFINED;
5339
5340   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5341     return;
5342
5343   if (IS_MOVING(x, y))
5344   {
5345     Moving2Blocked(x, y, &newx, &newy);
5346
5347     if (Tile[newx][newy] != EL_BLOCKED)
5348     {
5349       // element is moving, but target field is not free (blocked), but
5350       // already occupied by something different (example: acid pool);
5351       // in this case, only remove the moving field, but not the target
5352
5353       RemoveField(oldx, oldy);
5354
5355       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5356
5357       TEST_DrawLevelField(oldx, oldy);
5358
5359       return;
5360     }
5361   }
5362   else if (element == EL_BLOCKED)
5363   {
5364     Blocked2Moving(x, y, &oldx, &oldy);
5365     if (!IS_MOVING(oldx, oldy))
5366       return;
5367   }
5368
5369   if (element == EL_BLOCKED &&
5370       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5371        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5372        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5373        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5374        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5375        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5376     next_element = get_next_element(Tile[oldx][oldy]);
5377
5378   RemoveField(oldx, oldy);
5379   RemoveField(newx, newy);
5380
5381   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5382
5383   if (next_element != EL_UNDEFINED)
5384     Tile[oldx][oldy] = next_element;
5385
5386   TEST_DrawLevelField(oldx, oldy);
5387   TEST_DrawLevelField(newx, newy);
5388 }
5389
5390 void DrawDynamite(int x, int y)
5391 {
5392   int sx = SCREENX(x), sy = SCREENY(y);
5393   int graphic = el2img(Tile[x][y]);
5394   int frame;
5395
5396   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5397     return;
5398
5399   if (IS_WALKABLE_INSIDE(Back[x][y]))
5400     return;
5401
5402   if (Back[x][y])
5403     DrawLevelElement(x, y, Back[x][y]);
5404   else if (Store[x][y])
5405     DrawLevelElement(x, y, Store[x][y]);
5406   else if (game.use_masked_elements)
5407     DrawLevelElement(x, y, EL_EMPTY);
5408
5409   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5410
5411   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5412     DrawGraphicThruMask(sx, sy, graphic, frame);
5413   else
5414     DrawGraphic(sx, sy, graphic, frame);
5415 }
5416
5417 static void CheckDynamite(int x, int y)
5418 {
5419   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5420   {
5421     MovDelay[x][y]--;
5422
5423     if (MovDelay[x][y] != 0)
5424     {
5425       DrawDynamite(x, y);
5426       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5427
5428       return;
5429     }
5430   }
5431
5432   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5433
5434   Bang(x, y);
5435 }
5436
5437 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5438 {
5439   boolean num_checked_players = 0;
5440   int i;
5441
5442   for (i = 0; i < MAX_PLAYERS; i++)
5443   {
5444     if (stored_player[i].active)
5445     {
5446       int sx = stored_player[i].jx;
5447       int sy = stored_player[i].jy;
5448
5449       if (num_checked_players == 0)
5450       {
5451         *sx1 = *sx2 = sx;
5452         *sy1 = *sy2 = sy;
5453       }
5454       else
5455       {
5456         *sx1 = MIN(*sx1, sx);
5457         *sy1 = MIN(*sy1, sy);
5458         *sx2 = MAX(*sx2, sx);
5459         *sy2 = MAX(*sy2, sy);
5460       }
5461
5462       num_checked_players++;
5463     }
5464   }
5465 }
5466
5467 static boolean checkIfAllPlayersFitToScreen_RND(void)
5468 {
5469   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5470
5471   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5472
5473   return (sx2 - sx1 < SCR_FIELDX &&
5474           sy2 - sy1 < SCR_FIELDY);
5475 }
5476
5477 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5478 {
5479   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5480
5481   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5482
5483   *sx = (sx1 + sx2) / 2;
5484   *sy = (sy1 + sy2) / 2;
5485 }
5486
5487 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5488                                boolean center_screen, boolean quick_relocation)
5489 {
5490   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5491   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5492   boolean no_delay = (tape.warp_forward);
5493   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5494   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5495   int new_scroll_x, new_scroll_y;
5496
5497   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5498   {
5499     // case 1: quick relocation inside visible screen (without scrolling)
5500
5501     RedrawPlayfield();
5502
5503     return;
5504   }
5505
5506   if (!level.shifted_relocation || center_screen)
5507   {
5508     // relocation _with_ centering of screen
5509
5510     new_scroll_x = SCROLL_POSITION_X(x);
5511     new_scroll_y = SCROLL_POSITION_Y(y);
5512   }
5513   else
5514   {
5515     // relocation _without_ centering of screen
5516
5517     int center_scroll_x = SCROLL_POSITION_X(old_x);
5518     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5519     int offset_x = x + (scroll_x - center_scroll_x);
5520     int offset_y = y + (scroll_y - center_scroll_y);
5521
5522     // for new screen position, apply previous offset to center position
5523     new_scroll_x = SCROLL_POSITION_X(offset_x);
5524     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5525   }
5526
5527   if (quick_relocation)
5528   {
5529     // case 2: quick relocation (redraw without visible scrolling)
5530
5531     scroll_x = new_scroll_x;
5532     scroll_y = new_scroll_y;
5533
5534     RedrawPlayfield();
5535
5536     return;
5537   }
5538
5539   // case 3: visible relocation (with scrolling to new position)
5540
5541   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5542
5543   SetVideoFrameDelay(wait_delay_value);
5544
5545   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5546   {
5547     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5548     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5549
5550     if (dx == 0 && dy == 0)             // no scrolling needed at all
5551       break;
5552
5553     scroll_x -= dx;
5554     scroll_y -= dy;
5555
5556     // set values for horizontal/vertical screen scrolling (half tile size)
5557     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5558     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5559     int pos_x = dx * TILEX / 2;
5560     int pos_y = dy * TILEY / 2;
5561     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5562     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5563
5564     ScrollLevel(dx, dy);
5565     DrawAllPlayers();
5566
5567     // scroll in two steps of half tile size to make things smoother
5568     BlitScreenToBitmapExt_RND(window, fx, fy);
5569
5570     // scroll second step to align at full tile size
5571     BlitScreenToBitmap(window);
5572   }
5573
5574   DrawAllPlayers();
5575   BackToFront();
5576
5577   SetVideoFrameDelay(frame_delay_value_old);
5578 }
5579
5580 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5581 {
5582   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5583   int player_nr = GET_PLAYER_NR(el_player);
5584   struct PlayerInfo *player = &stored_player[player_nr];
5585   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5586   boolean no_delay = (tape.warp_forward);
5587   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5588   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5589   int old_jx = player->jx;
5590   int old_jy = player->jy;
5591   int old_element = Tile[old_jx][old_jy];
5592   int element = Tile[jx][jy];
5593   boolean player_relocated = (old_jx != jx || old_jy != jy);
5594
5595   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5596   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5597   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5598   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5599   int leave_side_horiz = move_dir_horiz;
5600   int leave_side_vert  = move_dir_vert;
5601   int enter_side = enter_side_horiz | enter_side_vert;
5602   int leave_side = leave_side_horiz | leave_side_vert;
5603
5604   if (player->buried)           // do not reanimate dead player
5605     return;
5606
5607   if (!player_relocated)        // no need to relocate the player
5608     return;
5609
5610   if (IS_PLAYER(jx, jy))        // player already placed at new position
5611   {
5612     RemoveField(jx, jy);        // temporarily remove newly placed player
5613     DrawLevelField(jx, jy);
5614   }
5615
5616   if (player->present)
5617   {
5618     while (player->MovPos)
5619     {
5620       ScrollPlayer(player, SCROLL_GO_ON);
5621       ScrollScreen(NULL, SCROLL_GO_ON);
5622
5623       AdvanceFrameAndPlayerCounters(player->index_nr);
5624
5625       DrawPlayer(player);
5626
5627       BackToFront_WithFrameDelay(wait_delay_value);
5628     }
5629
5630     DrawPlayer(player);         // needed here only to cleanup last field
5631     DrawLevelField(player->jx, player->jy);     // remove player graphic
5632
5633     player->is_moving = FALSE;
5634   }
5635
5636   if (IS_CUSTOM_ELEMENT(old_element))
5637     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5638                                CE_LEFT_BY_PLAYER,
5639                                player->index_bit, leave_side);
5640
5641   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5642                                       CE_PLAYER_LEAVES_X,
5643                                       player->index_bit, leave_side);
5644
5645   Tile[jx][jy] = el_player;
5646   InitPlayerField(jx, jy, el_player, TRUE);
5647
5648   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5649      possible that the relocation target field did not contain a player element,
5650      but a walkable element, to which the new player was relocated -- in this
5651      case, restore that (already initialized!) element on the player field */
5652   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5653   {
5654     Tile[jx][jy] = element;     // restore previously existing element
5655   }
5656
5657   // only visually relocate centered player
5658   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5659                      FALSE, level.instant_relocation);
5660
5661   TestIfPlayerTouchesBadThing(jx, jy);
5662   TestIfPlayerTouchesCustomElement(jx, jy);
5663
5664   if (IS_CUSTOM_ELEMENT(element))
5665     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5666                                player->index_bit, enter_side);
5667
5668   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5669                                       player->index_bit, enter_side);
5670
5671   if (player->is_switching)
5672   {
5673     /* ensure that relocation while still switching an element does not cause
5674        a new element to be treated as also switched directly after relocation
5675        (this is important for teleporter switches that teleport the player to
5676        a place where another teleporter switch is in the same direction, which
5677        would then incorrectly be treated as immediately switched before the
5678        direction key that caused the switch was released) */
5679
5680     player->switch_x += jx - old_jx;
5681     player->switch_y += jy - old_jy;
5682   }
5683 }
5684
5685 static void Explode(int ex, int ey, int phase, int mode)
5686 {
5687   int x, y;
5688   int last_phase;
5689   int border_element;
5690
5691   // !!! eliminate this variable !!!
5692   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5693
5694   if (game.explosions_delayed)
5695   {
5696     ExplodeField[ex][ey] = mode;
5697     return;
5698   }
5699
5700   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5701   {
5702     int center_element = Tile[ex][ey];
5703     int artwork_element, explosion_element;     // set these values later
5704
5705     // remove things displayed in background while burning dynamite
5706     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5707       Back[ex][ey] = 0;
5708
5709     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5710     {
5711       // put moving element to center field (and let it explode there)
5712       center_element = MovingOrBlocked2Element(ex, ey);
5713       RemoveMovingField(ex, ey);
5714       Tile[ex][ey] = center_element;
5715     }
5716
5717     // now "center_element" is finally determined -- set related values now
5718     artwork_element = center_element;           // for custom player artwork
5719     explosion_element = center_element;         // for custom player artwork
5720
5721     if (IS_PLAYER(ex, ey))
5722     {
5723       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5724
5725       artwork_element = stored_player[player_nr].artwork_element;
5726
5727       if (level.use_explosion_element[player_nr])
5728       {
5729         explosion_element = level.explosion_element[player_nr];
5730         artwork_element = explosion_element;
5731       }
5732     }
5733
5734     if (mode == EX_TYPE_NORMAL ||
5735         mode == EX_TYPE_CENTER ||
5736         mode == EX_TYPE_CROSS)
5737       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5738
5739     last_phase = element_info[explosion_element].explosion_delay + 1;
5740
5741     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5742     {
5743       int xx = x - ex + 1;
5744       int yy = y - ey + 1;
5745       int element;
5746
5747       if (!IN_LEV_FIELD(x, y) ||
5748           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5749           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5750         continue;
5751
5752       element = Tile[x][y];
5753
5754       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5755       {
5756         element = MovingOrBlocked2Element(x, y);
5757
5758         if (!IS_EXPLOSION_PROOF(element))
5759           RemoveMovingField(x, y);
5760       }
5761
5762       // indestructible elements can only explode in center (but not flames)
5763       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5764                                            mode == EX_TYPE_BORDER)) ||
5765           element == EL_FLAMES)
5766         continue;
5767
5768       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5769          behaviour, for example when touching a yamyam that explodes to rocks
5770          with active deadly shield, a rock is created under the player !!! */
5771       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5772 #if 0
5773       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5774           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5775            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5776 #else
5777       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5778 #endif
5779       {
5780         if (IS_ACTIVE_BOMB(element))
5781         {
5782           // re-activate things under the bomb like gate or penguin
5783           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5784           Back[x][y] = 0;
5785         }
5786
5787         continue;
5788       }
5789
5790       // save walkable background elements while explosion on same tile
5791       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5792           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5793         Back[x][y] = element;
5794
5795       // ignite explodable elements reached by other explosion
5796       if (element == EL_EXPLOSION)
5797         element = Store2[x][y];
5798
5799       if (AmoebaNr[x][y] &&
5800           (element == EL_AMOEBA_FULL ||
5801            element == EL_BD_AMOEBA ||
5802            element == EL_AMOEBA_GROWING))
5803       {
5804         AmoebaCnt[AmoebaNr[x][y]]--;
5805         AmoebaCnt2[AmoebaNr[x][y]]--;
5806       }
5807
5808       RemoveField(x, y);
5809
5810       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5811       {
5812         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5813
5814         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5815
5816         if (PLAYERINFO(ex, ey)->use_murphy)
5817           Store[x][y] = EL_EMPTY;
5818       }
5819
5820       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5821       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5822       else if (ELEM_IS_PLAYER(center_element))
5823         Store[x][y] = EL_EMPTY;
5824       else if (center_element == EL_YAMYAM)
5825         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5826       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5827         Store[x][y] = element_info[center_element].content.e[xx][yy];
5828 #if 1
5829       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5830       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5831       // otherwise) -- FIX THIS !!!
5832       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5833         Store[x][y] = element_info[element].content.e[1][1];
5834 #else
5835       else if (!CAN_EXPLODE(element))
5836         Store[x][y] = element_info[element].content.e[1][1];
5837 #endif
5838       else
5839         Store[x][y] = EL_EMPTY;
5840
5841       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5842           center_element == EL_AMOEBA_TO_DIAMOND)
5843         Store2[x][y] = element;
5844
5845       Tile[x][y] = EL_EXPLOSION;
5846       GfxElement[x][y] = artwork_element;
5847
5848       ExplodePhase[x][y] = 1;
5849       ExplodeDelay[x][y] = last_phase;
5850
5851       Stop[x][y] = TRUE;
5852     }
5853
5854     if (center_element == EL_YAMYAM)
5855       game.yamyam_content_nr =
5856         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5857
5858     return;
5859   }
5860
5861   if (Stop[ex][ey])
5862     return;
5863
5864   x = ex;
5865   y = ey;
5866
5867   if (phase == 1)
5868     GfxFrame[x][y] = 0;         // restart explosion animation
5869
5870   last_phase = ExplodeDelay[x][y];
5871
5872   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5873
5874   // this can happen if the player leaves an explosion just in time
5875   if (GfxElement[x][y] == EL_UNDEFINED)
5876     GfxElement[x][y] = EL_EMPTY;
5877
5878   border_element = Store2[x][y];
5879   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5880     border_element = StorePlayer[x][y];
5881
5882   if (phase == element_info[border_element].ignition_delay ||
5883       phase == last_phase)
5884   {
5885     boolean border_explosion = FALSE;
5886
5887     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5888         !PLAYER_EXPLOSION_PROTECTED(x, y))
5889     {
5890       KillPlayerUnlessExplosionProtected(x, y);
5891       border_explosion = TRUE;
5892     }
5893     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5894     {
5895       Tile[x][y] = Store2[x][y];
5896       Store2[x][y] = 0;
5897       Bang(x, y);
5898       border_explosion = TRUE;
5899     }
5900     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5901     {
5902       AmoebaToDiamond(x, y);
5903       Store2[x][y] = 0;
5904       border_explosion = TRUE;
5905     }
5906
5907     // if an element just explodes due to another explosion (chain-reaction),
5908     // do not immediately end the new explosion when it was the last frame of
5909     // the explosion (as it would be done in the following "if"-statement!)
5910     if (border_explosion && phase == last_phase)
5911       return;
5912   }
5913
5914   if (phase == last_phase)
5915   {
5916     int element;
5917
5918     element = Tile[x][y] = Store[x][y];
5919     Store[x][y] = Store2[x][y] = 0;
5920     GfxElement[x][y] = EL_UNDEFINED;
5921
5922     // player can escape from explosions and might therefore be still alive
5923     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5924         element <= EL_PLAYER_IS_EXPLODING_4)
5925     {
5926       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5927       int explosion_element = EL_PLAYER_1 + player_nr;
5928       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5929       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5930
5931       if (level.use_explosion_element[player_nr])
5932         explosion_element = level.explosion_element[player_nr];
5933
5934       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5935                     element_info[explosion_element].content.e[xx][yy]);
5936     }
5937
5938     // restore probably existing indestructible background element
5939     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5940       element = Tile[x][y] = Back[x][y];
5941     Back[x][y] = 0;
5942
5943     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5944     GfxDir[x][y] = MV_NONE;
5945     ChangeDelay[x][y] = 0;
5946     ChangePage[x][y] = -1;
5947
5948     CustomValue[x][y] = 0;
5949
5950     InitField_WithBug2(x, y, FALSE);
5951
5952     TEST_DrawLevelField(x, y);
5953
5954     TestIfElementTouchesCustomElement(x, y);
5955
5956     if (GFX_CRUMBLED(element))
5957       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5958
5959     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5960       StorePlayer[x][y] = 0;
5961
5962     if (ELEM_IS_PLAYER(element))
5963       RelocatePlayer(x, y, element);
5964   }
5965   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5966   {
5967     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5968     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5969
5970     if (phase == delay)
5971       TEST_DrawLevelFieldCrumbled(x, y);
5972
5973     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5974     {
5975       DrawLevelElement(x, y, Back[x][y]);
5976       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5977     }
5978     else if (IS_WALKABLE_UNDER(Back[x][y]))
5979     {
5980       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5981       DrawLevelElementThruMask(x, y, Back[x][y]);
5982     }
5983     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5984       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5985   }
5986 }
5987
5988 static void DynaExplode(int ex, int ey)
5989 {
5990   int i, j;
5991   int dynabomb_element = Tile[ex][ey];
5992   int dynabomb_size = 1;
5993   boolean dynabomb_xl = FALSE;
5994   struct PlayerInfo *player;
5995   static int xy[4][2] =
5996   {
5997     { 0, -1 },
5998     { -1, 0 },
5999     { +1, 0 },
6000     { 0, +1 }
6001   };
6002
6003   if (IS_ACTIVE_BOMB(dynabomb_element))
6004   {
6005     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6006     dynabomb_size = player->dynabomb_size;
6007     dynabomb_xl = player->dynabomb_xl;
6008     player->dynabombs_left++;
6009   }
6010
6011   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6012
6013   for (i = 0; i < NUM_DIRECTIONS; i++)
6014   {
6015     for (j = 1; j <= dynabomb_size; j++)
6016     {
6017       int x = ex + j * xy[i][0];
6018       int y = ey + j * xy[i][1];
6019       int element;
6020
6021       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6022         break;
6023
6024       element = Tile[x][y];
6025
6026       // do not restart explosions of fields with active bombs
6027       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6028         continue;
6029
6030       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6031
6032       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6033           !IS_DIGGABLE(element) && !dynabomb_xl)
6034         break;
6035     }
6036   }
6037 }
6038
6039 void Bang(int x, int y)
6040 {
6041   int element = MovingOrBlocked2Element(x, y);
6042   int explosion_type = EX_TYPE_NORMAL;
6043
6044   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6045   {
6046     struct PlayerInfo *player = PLAYERINFO(x, y);
6047
6048     element = Tile[x][y] = player->initial_element;
6049
6050     if (level.use_explosion_element[player->index_nr])
6051     {
6052       int explosion_element = level.explosion_element[player->index_nr];
6053
6054       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6055         explosion_type = EX_TYPE_CROSS;
6056       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6057         explosion_type = EX_TYPE_CENTER;
6058     }
6059   }
6060
6061   switch (element)
6062   {
6063     case EL_BUG:
6064     case EL_SPACESHIP:
6065     case EL_BD_BUTTERFLY:
6066     case EL_BD_FIREFLY:
6067     case EL_YAMYAM:
6068     case EL_DARK_YAMYAM:
6069     case EL_ROBOT:
6070     case EL_PACMAN:
6071     case EL_MOLE:
6072       RaiseScoreElement(element);
6073       break;
6074
6075     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6076     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6077     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6078     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6079     case EL_DYNABOMB_INCREASE_NUMBER:
6080     case EL_DYNABOMB_INCREASE_SIZE:
6081     case EL_DYNABOMB_INCREASE_POWER:
6082       explosion_type = EX_TYPE_DYNA;
6083       break;
6084
6085     case EL_DC_LANDMINE:
6086       explosion_type = EX_TYPE_CENTER;
6087       break;
6088
6089     case EL_PENGUIN:
6090     case EL_LAMP:
6091     case EL_LAMP_ACTIVE:
6092     case EL_AMOEBA_TO_DIAMOND:
6093       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6094         explosion_type = EX_TYPE_CENTER;
6095       break;
6096
6097     default:
6098       if (element_info[element].explosion_type == EXPLODES_CROSS)
6099         explosion_type = EX_TYPE_CROSS;
6100       else if (element_info[element].explosion_type == EXPLODES_1X1)
6101         explosion_type = EX_TYPE_CENTER;
6102       break;
6103   }
6104
6105   if (explosion_type == EX_TYPE_DYNA)
6106     DynaExplode(x, y);
6107   else
6108     Explode(x, y, EX_PHASE_START, explosion_type);
6109
6110   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6111 }
6112
6113 static void SplashAcid(int x, int y)
6114 {
6115   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6116       (!IN_LEV_FIELD(x - 1, y - 2) ||
6117        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6118     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6119
6120   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6121       (!IN_LEV_FIELD(x + 1, y - 2) ||
6122        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6123     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6124
6125   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6126 }
6127
6128 static void InitBeltMovement(void)
6129 {
6130   static int belt_base_element[4] =
6131   {
6132     EL_CONVEYOR_BELT_1_LEFT,
6133     EL_CONVEYOR_BELT_2_LEFT,
6134     EL_CONVEYOR_BELT_3_LEFT,
6135     EL_CONVEYOR_BELT_4_LEFT
6136   };
6137   static int belt_base_active_element[4] =
6138   {
6139     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6140     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6141     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6142     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6143   };
6144
6145   int x, y, i, j;
6146
6147   // set frame order for belt animation graphic according to belt direction
6148   for (i = 0; i < NUM_BELTS; i++)
6149   {
6150     int belt_nr = i;
6151
6152     for (j = 0; j < NUM_BELT_PARTS; j++)
6153     {
6154       int element = belt_base_active_element[belt_nr] + j;
6155       int graphic_1 = el2img(element);
6156       int graphic_2 = el2panelimg(element);
6157
6158       if (game.belt_dir[i] == MV_LEFT)
6159       {
6160         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6161         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6162       }
6163       else
6164       {
6165         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6166         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6167       }
6168     }
6169   }
6170
6171   SCAN_PLAYFIELD(x, y)
6172   {
6173     int element = Tile[x][y];
6174
6175     for (i = 0; i < NUM_BELTS; i++)
6176     {
6177       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6178       {
6179         int e_belt_nr = getBeltNrFromBeltElement(element);
6180         int belt_nr = i;
6181
6182         if (e_belt_nr == belt_nr)
6183         {
6184           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6185
6186           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6187         }
6188       }
6189     }
6190   }
6191 }
6192
6193 static void ToggleBeltSwitch(int x, int y)
6194 {
6195   static int belt_base_element[4] =
6196   {
6197     EL_CONVEYOR_BELT_1_LEFT,
6198     EL_CONVEYOR_BELT_2_LEFT,
6199     EL_CONVEYOR_BELT_3_LEFT,
6200     EL_CONVEYOR_BELT_4_LEFT
6201   };
6202   static int belt_base_active_element[4] =
6203   {
6204     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6205     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6206     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6207     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6208   };
6209   static int belt_base_switch_element[4] =
6210   {
6211     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6212     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6213     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6214     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6215   };
6216   static int belt_move_dir[4] =
6217   {
6218     MV_LEFT,
6219     MV_NONE,
6220     MV_RIGHT,
6221     MV_NONE,
6222   };
6223
6224   int element = Tile[x][y];
6225   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6226   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6227   int belt_dir = belt_move_dir[belt_dir_nr];
6228   int xx, yy, i;
6229
6230   if (!IS_BELT_SWITCH(element))
6231     return;
6232
6233   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6234   game.belt_dir[belt_nr] = belt_dir;
6235
6236   if (belt_dir_nr == 3)
6237     belt_dir_nr = 1;
6238
6239   // set frame order for belt animation graphic according to belt direction
6240   for (i = 0; i < NUM_BELT_PARTS; i++)
6241   {
6242     int element = belt_base_active_element[belt_nr] + i;
6243     int graphic_1 = el2img(element);
6244     int graphic_2 = el2panelimg(element);
6245
6246     if (belt_dir == MV_LEFT)
6247     {
6248       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6249       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6250     }
6251     else
6252     {
6253       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6254       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6255     }
6256   }
6257
6258   SCAN_PLAYFIELD(xx, yy)
6259   {
6260     int element = Tile[xx][yy];
6261
6262     if (IS_BELT_SWITCH(element))
6263     {
6264       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6265
6266       if (e_belt_nr == belt_nr)
6267       {
6268         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6269         TEST_DrawLevelField(xx, yy);
6270       }
6271     }
6272     else if (IS_BELT(element) && belt_dir != MV_NONE)
6273     {
6274       int e_belt_nr = getBeltNrFromBeltElement(element);
6275
6276       if (e_belt_nr == belt_nr)
6277       {
6278         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6279
6280         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6281         TEST_DrawLevelField(xx, yy);
6282       }
6283     }
6284     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6285     {
6286       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6287
6288       if (e_belt_nr == belt_nr)
6289       {
6290         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6291
6292         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6293         TEST_DrawLevelField(xx, yy);
6294       }
6295     }
6296   }
6297 }
6298
6299 static void ToggleSwitchgateSwitch(int x, int y)
6300 {
6301   int xx, yy;
6302
6303   game.switchgate_pos = !game.switchgate_pos;
6304
6305   SCAN_PLAYFIELD(xx, yy)
6306   {
6307     int element = Tile[xx][yy];
6308
6309     if (element == EL_SWITCHGATE_SWITCH_UP)
6310     {
6311       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6312       TEST_DrawLevelField(xx, yy);
6313     }
6314     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6315     {
6316       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6317       TEST_DrawLevelField(xx, yy);
6318     }
6319     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6320     {
6321       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6322       TEST_DrawLevelField(xx, yy);
6323     }
6324     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6325     {
6326       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6327       TEST_DrawLevelField(xx, yy);
6328     }
6329     else if (element == EL_SWITCHGATE_OPEN ||
6330              element == EL_SWITCHGATE_OPENING)
6331     {
6332       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6333
6334       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6335     }
6336     else if (element == EL_SWITCHGATE_CLOSED ||
6337              element == EL_SWITCHGATE_CLOSING)
6338     {
6339       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6340
6341       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6342     }
6343   }
6344 }
6345
6346 static int getInvisibleActiveFromInvisibleElement(int element)
6347 {
6348   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6349           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6350           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6351           element);
6352 }
6353
6354 static int getInvisibleFromInvisibleActiveElement(int element)
6355 {
6356   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6357           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6358           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6359           element);
6360 }
6361
6362 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6363 {
6364   int x, y;
6365
6366   SCAN_PLAYFIELD(x, y)
6367   {
6368     int element = Tile[x][y];
6369
6370     if (element == EL_LIGHT_SWITCH &&
6371         game.light_time_left > 0)
6372     {
6373       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6374       TEST_DrawLevelField(x, y);
6375     }
6376     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6377              game.light_time_left == 0)
6378     {
6379       Tile[x][y] = EL_LIGHT_SWITCH;
6380       TEST_DrawLevelField(x, y);
6381     }
6382     else if (element == EL_EMC_DRIPPER &&
6383              game.light_time_left > 0)
6384     {
6385       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6386       TEST_DrawLevelField(x, y);
6387     }
6388     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6389              game.light_time_left == 0)
6390     {
6391       Tile[x][y] = EL_EMC_DRIPPER;
6392       TEST_DrawLevelField(x, y);
6393     }
6394     else if (element == EL_INVISIBLE_STEELWALL ||
6395              element == EL_INVISIBLE_WALL ||
6396              element == EL_INVISIBLE_SAND)
6397     {
6398       if (game.light_time_left > 0)
6399         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6400
6401       TEST_DrawLevelField(x, y);
6402
6403       // uncrumble neighbour fields, if needed
6404       if (element == EL_INVISIBLE_SAND)
6405         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6406     }
6407     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6408              element == EL_INVISIBLE_WALL_ACTIVE ||
6409              element == EL_INVISIBLE_SAND_ACTIVE)
6410     {
6411       if (game.light_time_left == 0)
6412         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6413
6414       TEST_DrawLevelField(x, y);
6415
6416       // re-crumble neighbour fields, if needed
6417       if (element == EL_INVISIBLE_SAND)
6418         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6419     }
6420   }
6421 }
6422
6423 static void RedrawAllInvisibleElementsForLenses(void)
6424 {
6425   int x, y;
6426
6427   SCAN_PLAYFIELD(x, y)
6428   {
6429     int element = Tile[x][y];
6430
6431     if (element == EL_EMC_DRIPPER &&
6432         game.lenses_time_left > 0)
6433     {
6434       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6435       TEST_DrawLevelField(x, y);
6436     }
6437     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6438              game.lenses_time_left == 0)
6439     {
6440       Tile[x][y] = EL_EMC_DRIPPER;
6441       TEST_DrawLevelField(x, y);
6442     }
6443     else if (element == EL_INVISIBLE_STEELWALL ||
6444              element == EL_INVISIBLE_WALL ||
6445              element == EL_INVISIBLE_SAND)
6446     {
6447       if (game.lenses_time_left > 0)
6448         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6449
6450       TEST_DrawLevelField(x, y);
6451
6452       // uncrumble neighbour fields, if needed
6453       if (element == EL_INVISIBLE_SAND)
6454         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6455     }
6456     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6457              element == EL_INVISIBLE_WALL_ACTIVE ||
6458              element == EL_INVISIBLE_SAND_ACTIVE)
6459     {
6460       if (game.lenses_time_left == 0)
6461         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6462
6463       TEST_DrawLevelField(x, y);
6464
6465       // re-crumble neighbour fields, if needed
6466       if (element == EL_INVISIBLE_SAND)
6467         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6468     }
6469   }
6470 }
6471
6472 static void RedrawAllInvisibleElementsForMagnifier(void)
6473 {
6474   int x, y;
6475
6476   SCAN_PLAYFIELD(x, y)
6477   {
6478     int element = Tile[x][y];
6479
6480     if (element == EL_EMC_FAKE_GRASS &&
6481         game.magnify_time_left > 0)
6482     {
6483       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6484       TEST_DrawLevelField(x, y);
6485     }
6486     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6487              game.magnify_time_left == 0)
6488     {
6489       Tile[x][y] = EL_EMC_FAKE_GRASS;
6490       TEST_DrawLevelField(x, y);
6491     }
6492     else if (IS_GATE_GRAY(element) &&
6493              game.magnify_time_left > 0)
6494     {
6495       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6496                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6497                     IS_EM_GATE_GRAY(element) ?
6498                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6499                     IS_EMC_GATE_GRAY(element) ?
6500                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6501                     IS_DC_GATE_GRAY(element) ?
6502                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6503                     element);
6504       TEST_DrawLevelField(x, y);
6505     }
6506     else if (IS_GATE_GRAY_ACTIVE(element) &&
6507              game.magnify_time_left == 0)
6508     {
6509       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6510                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6511                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6512                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6513                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6514                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6515                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6516                     EL_DC_GATE_WHITE_GRAY :
6517                     element);
6518       TEST_DrawLevelField(x, y);
6519     }
6520   }
6521 }
6522
6523 static void ToggleLightSwitch(int x, int y)
6524 {
6525   int element = Tile[x][y];
6526
6527   game.light_time_left =
6528     (element == EL_LIGHT_SWITCH ?
6529      level.time_light * FRAMES_PER_SECOND : 0);
6530
6531   RedrawAllLightSwitchesAndInvisibleElements();
6532 }
6533
6534 static void ActivateTimegateSwitch(int x, int y)
6535 {
6536   int xx, yy;
6537
6538   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6539
6540   SCAN_PLAYFIELD(xx, yy)
6541   {
6542     int element = Tile[xx][yy];
6543
6544     if (element == EL_TIMEGATE_CLOSED ||
6545         element == EL_TIMEGATE_CLOSING)
6546     {
6547       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6548       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6549     }
6550
6551     /*
6552     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6553     {
6554       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6555       TEST_DrawLevelField(xx, yy);
6556     }
6557     */
6558
6559   }
6560
6561   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6562                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6563 }
6564
6565 static void Impact(int x, int y)
6566 {
6567   boolean last_line = (y == lev_fieldy - 1);
6568   boolean object_hit = FALSE;
6569   boolean impact = (last_line || object_hit);
6570   int element = Tile[x][y];
6571   int smashed = EL_STEELWALL;
6572
6573   if (!last_line)       // check if element below was hit
6574   {
6575     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6576       return;
6577
6578     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6579                                          MovDir[x][y + 1] != MV_DOWN ||
6580                                          MovPos[x][y + 1] <= TILEY / 2));
6581
6582     // do not smash moving elements that left the smashed field in time
6583     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6584         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6585       object_hit = FALSE;
6586
6587 #if USE_QUICKSAND_IMPACT_BUGFIX
6588     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6589     {
6590       RemoveMovingField(x, y + 1);
6591       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6592       Tile[x][y + 2] = EL_ROCK;
6593       TEST_DrawLevelField(x, y + 2);
6594
6595       object_hit = TRUE;
6596     }
6597
6598     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6599     {
6600       RemoveMovingField(x, y + 1);
6601       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6602       Tile[x][y + 2] = EL_ROCK;
6603       TEST_DrawLevelField(x, y + 2);
6604
6605       object_hit = TRUE;
6606     }
6607 #endif
6608
6609     if (object_hit)
6610       smashed = MovingOrBlocked2Element(x, y + 1);
6611
6612     impact = (last_line || object_hit);
6613   }
6614
6615   if (!last_line && smashed == EL_ACID) // element falls into acid
6616   {
6617     SplashAcid(x, y + 1);
6618     return;
6619   }
6620
6621   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6622   // only reset graphic animation if graphic really changes after impact
6623   if (impact &&
6624       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6625   {
6626     ResetGfxAnimation(x, y);
6627     TEST_DrawLevelField(x, y);
6628   }
6629
6630   if (impact && CAN_EXPLODE_IMPACT(element))
6631   {
6632     Bang(x, y);
6633     return;
6634   }
6635   else if (impact && element == EL_PEARL &&
6636            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6637   {
6638     ResetGfxAnimation(x, y);
6639
6640     Tile[x][y] = EL_PEARL_BREAKING;
6641     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6642     return;
6643   }
6644   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6645   {
6646     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6647
6648     return;
6649   }
6650
6651   if (impact && element == EL_AMOEBA_DROP)
6652   {
6653     if (object_hit && IS_PLAYER(x, y + 1))
6654       KillPlayerUnlessEnemyProtected(x, y + 1);
6655     else if (object_hit && smashed == EL_PENGUIN)
6656       Bang(x, y + 1);
6657     else
6658     {
6659       Tile[x][y] = EL_AMOEBA_GROWING;
6660       Store[x][y] = EL_AMOEBA_WET;
6661
6662       ResetRandomAnimationValue(x, y);
6663     }
6664     return;
6665   }
6666
6667   if (object_hit)               // check which object was hit
6668   {
6669     if ((CAN_PASS_MAGIC_WALL(element) && 
6670          (smashed == EL_MAGIC_WALL ||
6671           smashed == EL_BD_MAGIC_WALL)) ||
6672         (CAN_PASS_DC_MAGIC_WALL(element) &&
6673          smashed == EL_DC_MAGIC_WALL))
6674     {
6675       int xx, yy;
6676       int activated_magic_wall =
6677         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6678          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6679          EL_DC_MAGIC_WALL_ACTIVE);
6680
6681       // activate magic wall / mill
6682       SCAN_PLAYFIELD(xx, yy)
6683       {
6684         if (Tile[xx][yy] == smashed)
6685           Tile[xx][yy] = activated_magic_wall;
6686       }
6687
6688       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6689       game.magic_wall_active = TRUE;
6690
6691       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6692                             SND_MAGIC_WALL_ACTIVATING :
6693                             smashed == EL_BD_MAGIC_WALL ?
6694                             SND_BD_MAGIC_WALL_ACTIVATING :
6695                             SND_DC_MAGIC_WALL_ACTIVATING));
6696     }
6697
6698     if (IS_PLAYER(x, y + 1))
6699     {
6700       if (CAN_SMASH_PLAYER(element))
6701       {
6702         KillPlayerUnlessEnemyProtected(x, y + 1);
6703         return;
6704       }
6705     }
6706     else if (smashed == EL_PENGUIN)
6707     {
6708       if (CAN_SMASH_PLAYER(element))
6709       {
6710         Bang(x, y + 1);
6711         return;
6712       }
6713     }
6714     else if (element == EL_BD_DIAMOND)
6715     {
6716       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6717       {
6718         Bang(x, y + 1);
6719         return;
6720       }
6721     }
6722     else if (((element == EL_SP_INFOTRON ||
6723                element == EL_SP_ZONK) &&
6724               (smashed == EL_SP_SNIKSNAK ||
6725                smashed == EL_SP_ELECTRON ||
6726                smashed == EL_SP_DISK_ORANGE)) ||
6727              (element == EL_SP_INFOTRON &&
6728               smashed == EL_SP_DISK_YELLOW))
6729     {
6730       Bang(x, y + 1);
6731       return;
6732     }
6733     else if (CAN_SMASH_EVERYTHING(element))
6734     {
6735       if (IS_CLASSIC_ENEMY(smashed) ||
6736           CAN_EXPLODE_SMASHED(smashed))
6737       {
6738         Bang(x, y + 1);
6739         return;
6740       }
6741       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6742       {
6743         if (smashed == EL_LAMP ||
6744             smashed == EL_LAMP_ACTIVE)
6745         {
6746           Bang(x, y + 1);
6747           return;
6748         }
6749         else if (smashed == EL_NUT)
6750         {
6751           Tile[x][y + 1] = EL_NUT_BREAKING;
6752           PlayLevelSound(x, y, SND_NUT_BREAKING);
6753           RaiseScoreElement(EL_NUT);
6754           return;
6755         }
6756         else if (smashed == EL_PEARL)
6757         {
6758           ResetGfxAnimation(x, y);
6759
6760           Tile[x][y + 1] = EL_PEARL_BREAKING;
6761           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6762           return;
6763         }
6764         else if (smashed == EL_DIAMOND)
6765         {
6766           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6767           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6768           return;
6769         }
6770         else if (IS_BELT_SWITCH(smashed))
6771         {
6772           ToggleBeltSwitch(x, y + 1);
6773         }
6774         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6775                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6776                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6777                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6778         {
6779           ToggleSwitchgateSwitch(x, y + 1);
6780         }
6781         else if (smashed == EL_LIGHT_SWITCH ||
6782                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6783         {
6784           ToggleLightSwitch(x, y + 1);
6785         }
6786         else
6787         {
6788           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6789
6790           CheckElementChangeBySide(x, y + 1, smashed, element,
6791                                    CE_SWITCHED, CH_SIDE_TOP);
6792           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6793                                             CH_SIDE_TOP);
6794         }
6795       }
6796       else
6797       {
6798         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6799       }
6800     }
6801   }
6802
6803   // play sound of magic wall / mill
6804   if (!last_line &&
6805       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6806        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6807        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6808   {
6809     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6810       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6811     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6812       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6813     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6814       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6815
6816     return;
6817   }
6818
6819   // play sound of object that hits the ground
6820   if (last_line || object_hit)
6821     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6822 }
6823
6824 static void TurnRoundExt(int x, int y)
6825 {
6826   static struct
6827   {
6828     int dx, dy;
6829   } move_xy[] =
6830   {
6831     {  0,  0 },
6832     { -1,  0 },
6833     { +1,  0 },
6834     {  0,  0 },
6835     {  0, -1 },
6836     {  0,  0 }, { 0, 0 }, { 0, 0 },
6837     {  0, +1 }
6838   };
6839   static struct
6840   {
6841     int left, right, back;
6842   } turn[] =
6843   {
6844     { 0,        0,              0        },
6845     { MV_DOWN,  MV_UP,          MV_RIGHT },
6846     { MV_UP,    MV_DOWN,        MV_LEFT  },
6847     { 0,        0,              0        },
6848     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6849     { 0,        0,              0        },
6850     { 0,        0,              0        },
6851     { 0,        0,              0        },
6852     { MV_RIGHT, MV_LEFT,        MV_UP    }
6853   };
6854
6855   int element = Tile[x][y];
6856   int move_pattern = element_info[element].move_pattern;
6857
6858   int old_move_dir = MovDir[x][y];
6859   int left_dir  = turn[old_move_dir].left;
6860   int right_dir = turn[old_move_dir].right;
6861   int back_dir  = turn[old_move_dir].back;
6862
6863   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6864   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6865   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6866   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6867
6868   int left_x  = x + left_dx,  left_y  = y + left_dy;
6869   int right_x = x + right_dx, right_y = y + right_dy;
6870   int move_x  = x + move_dx,  move_y  = y + move_dy;
6871
6872   int xx, yy;
6873
6874   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6875   {
6876     TestIfBadThingTouchesOtherBadThing(x, y);
6877
6878     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6879       MovDir[x][y] = right_dir;
6880     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6881       MovDir[x][y] = left_dir;
6882
6883     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6884       MovDelay[x][y] = 9;
6885     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6886       MovDelay[x][y] = 1;
6887   }
6888   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6889   {
6890     TestIfBadThingTouchesOtherBadThing(x, y);
6891
6892     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6893       MovDir[x][y] = left_dir;
6894     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6895       MovDir[x][y] = right_dir;
6896
6897     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6898       MovDelay[x][y] = 9;
6899     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6900       MovDelay[x][y] = 1;
6901   }
6902   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6903   {
6904     TestIfBadThingTouchesOtherBadThing(x, y);
6905
6906     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6907       MovDir[x][y] = left_dir;
6908     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6909       MovDir[x][y] = right_dir;
6910
6911     if (MovDir[x][y] != old_move_dir)
6912       MovDelay[x][y] = 9;
6913   }
6914   else if (element == EL_YAMYAM)
6915   {
6916     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6917     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6918
6919     if (can_turn_left && can_turn_right)
6920       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6921     else if (can_turn_left)
6922       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6923     else if (can_turn_right)
6924       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6925     else
6926       MovDir[x][y] = back_dir;
6927
6928     MovDelay[x][y] = 16 + 16 * RND(3);
6929   }
6930   else if (element == EL_DARK_YAMYAM)
6931   {
6932     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6933                                                          left_x, left_y);
6934     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6935                                                          right_x, right_y);
6936
6937     if (can_turn_left && can_turn_right)
6938       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6939     else if (can_turn_left)
6940       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6941     else if (can_turn_right)
6942       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6943     else
6944       MovDir[x][y] = back_dir;
6945
6946     MovDelay[x][y] = 16 + 16 * RND(3);
6947   }
6948   else if (element == EL_PACMAN)
6949   {
6950     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6951     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6952
6953     if (can_turn_left && can_turn_right)
6954       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6955     else if (can_turn_left)
6956       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6957     else if (can_turn_right)
6958       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6959     else
6960       MovDir[x][y] = back_dir;
6961
6962     MovDelay[x][y] = 6 + RND(40);
6963   }
6964   else if (element == EL_PIG)
6965   {
6966     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6967     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6968     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6969     boolean should_turn_left, should_turn_right, should_move_on;
6970     int rnd_value = 24;
6971     int rnd = RND(rnd_value);
6972
6973     should_turn_left = (can_turn_left &&
6974                         (!can_move_on ||
6975                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6976                                                    y + back_dy + left_dy)));
6977     should_turn_right = (can_turn_right &&
6978                          (!can_move_on ||
6979                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6980                                                     y + back_dy + right_dy)));
6981     should_move_on = (can_move_on &&
6982                       (!can_turn_left ||
6983                        !can_turn_right ||
6984                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6985                                                  y + move_dy + left_dy) ||
6986                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6987                                                  y + move_dy + right_dy)));
6988
6989     if (should_turn_left || should_turn_right || should_move_on)
6990     {
6991       if (should_turn_left && should_turn_right && should_move_on)
6992         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6993                         rnd < 2 * rnd_value / 3 ? right_dir :
6994                         old_move_dir);
6995       else if (should_turn_left && should_turn_right)
6996         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6997       else if (should_turn_left && should_move_on)
6998         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6999       else if (should_turn_right && should_move_on)
7000         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7001       else if (should_turn_left)
7002         MovDir[x][y] = left_dir;
7003       else if (should_turn_right)
7004         MovDir[x][y] = right_dir;
7005       else if (should_move_on)
7006         MovDir[x][y] = old_move_dir;
7007     }
7008     else if (can_move_on && rnd > rnd_value / 8)
7009       MovDir[x][y] = old_move_dir;
7010     else if (can_turn_left && can_turn_right)
7011       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7012     else if (can_turn_left && rnd > rnd_value / 8)
7013       MovDir[x][y] = left_dir;
7014     else if (can_turn_right && rnd > rnd_value/8)
7015       MovDir[x][y] = right_dir;
7016     else
7017       MovDir[x][y] = back_dir;
7018
7019     xx = x + move_xy[MovDir[x][y]].dx;
7020     yy = y + move_xy[MovDir[x][y]].dy;
7021
7022     if (!IN_LEV_FIELD(xx, yy) ||
7023         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7024       MovDir[x][y] = old_move_dir;
7025
7026     MovDelay[x][y] = 0;
7027   }
7028   else if (element == EL_DRAGON)
7029   {
7030     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7031     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7032     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7033     int rnd_value = 24;
7034     int rnd = RND(rnd_value);
7035
7036     if (can_move_on && rnd > rnd_value / 8)
7037       MovDir[x][y] = old_move_dir;
7038     else if (can_turn_left && can_turn_right)
7039       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7040     else if (can_turn_left && rnd > rnd_value / 8)
7041       MovDir[x][y] = left_dir;
7042     else if (can_turn_right && rnd > rnd_value / 8)
7043       MovDir[x][y] = right_dir;
7044     else
7045       MovDir[x][y] = back_dir;
7046
7047     xx = x + move_xy[MovDir[x][y]].dx;
7048     yy = y + move_xy[MovDir[x][y]].dy;
7049
7050     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7051       MovDir[x][y] = old_move_dir;
7052
7053     MovDelay[x][y] = 0;
7054   }
7055   else if (element == EL_MOLE)
7056   {
7057     boolean can_move_on =
7058       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7059                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7060                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7061     if (!can_move_on)
7062     {
7063       boolean can_turn_left =
7064         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7065                               IS_AMOEBOID(Tile[left_x][left_y])));
7066
7067       boolean can_turn_right =
7068         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7069                               IS_AMOEBOID(Tile[right_x][right_y])));
7070
7071       if (can_turn_left && can_turn_right)
7072         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7073       else if (can_turn_left)
7074         MovDir[x][y] = left_dir;
7075       else
7076         MovDir[x][y] = right_dir;
7077     }
7078
7079     if (MovDir[x][y] != old_move_dir)
7080       MovDelay[x][y] = 9;
7081   }
7082   else if (element == EL_BALLOON)
7083   {
7084     MovDir[x][y] = game.wind_direction;
7085     MovDelay[x][y] = 0;
7086   }
7087   else if (element == EL_SPRING)
7088   {
7089     if (MovDir[x][y] & MV_HORIZONTAL)
7090     {
7091       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7092           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7093       {
7094         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7095         ResetGfxAnimation(move_x, move_y);
7096         TEST_DrawLevelField(move_x, move_y);
7097
7098         MovDir[x][y] = back_dir;
7099       }
7100       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7101                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7102         MovDir[x][y] = MV_NONE;
7103     }
7104
7105     MovDelay[x][y] = 0;
7106   }
7107   else if (element == EL_ROBOT ||
7108            element == EL_SATELLITE ||
7109            element == EL_PENGUIN ||
7110            element == EL_EMC_ANDROID)
7111   {
7112     int attr_x = -1, attr_y = -1;
7113
7114     if (game.all_players_gone)
7115     {
7116       attr_x = game.exit_x;
7117       attr_y = game.exit_y;
7118     }
7119     else
7120     {
7121       int i;
7122
7123       for (i = 0; i < MAX_PLAYERS; i++)
7124       {
7125         struct PlayerInfo *player = &stored_player[i];
7126         int jx = player->jx, jy = player->jy;
7127
7128         if (!player->active)
7129           continue;
7130
7131         if (attr_x == -1 ||
7132             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7133         {
7134           attr_x = jx;
7135           attr_y = jy;
7136         }
7137       }
7138     }
7139
7140     if (element == EL_ROBOT &&
7141         game.robot_wheel_x >= 0 &&
7142         game.robot_wheel_y >= 0 &&
7143         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7144          game.engine_version < VERSION_IDENT(3,1,0,0)))
7145     {
7146       attr_x = game.robot_wheel_x;
7147       attr_y = game.robot_wheel_y;
7148     }
7149
7150     if (element == EL_PENGUIN)
7151     {
7152       int i;
7153       static int xy[4][2] =
7154       {
7155         { 0, -1 },
7156         { -1, 0 },
7157         { +1, 0 },
7158         { 0, +1 }
7159       };
7160
7161       for (i = 0; i < NUM_DIRECTIONS; i++)
7162       {
7163         int ex = x + xy[i][0];
7164         int ey = y + xy[i][1];
7165
7166         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7167                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7168                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7169                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7170         {
7171           attr_x = ex;
7172           attr_y = ey;
7173           break;
7174         }
7175       }
7176     }
7177
7178     MovDir[x][y] = MV_NONE;
7179     if (attr_x < x)
7180       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7181     else if (attr_x > x)
7182       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7183     if (attr_y < y)
7184       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7185     else if (attr_y > y)
7186       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7187
7188     if (element == EL_ROBOT)
7189     {
7190       int newx, newy;
7191
7192       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7193         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7194       Moving2Blocked(x, y, &newx, &newy);
7195
7196       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7197         MovDelay[x][y] = 8 + 8 * !RND(3);
7198       else
7199         MovDelay[x][y] = 16;
7200     }
7201     else if (element == EL_PENGUIN)
7202     {
7203       int newx, newy;
7204
7205       MovDelay[x][y] = 1;
7206
7207       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7208       {
7209         boolean first_horiz = RND(2);
7210         int new_move_dir = MovDir[x][y];
7211
7212         MovDir[x][y] =
7213           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7214         Moving2Blocked(x, y, &newx, &newy);
7215
7216         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7217           return;
7218
7219         MovDir[x][y] =
7220           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7221         Moving2Blocked(x, y, &newx, &newy);
7222
7223         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7224           return;
7225
7226         MovDir[x][y] = old_move_dir;
7227         return;
7228       }
7229     }
7230     else if (element == EL_SATELLITE)
7231     {
7232       int newx, newy;
7233
7234       MovDelay[x][y] = 1;
7235
7236       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7237       {
7238         boolean first_horiz = RND(2);
7239         int new_move_dir = MovDir[x][y];
7240
7241         MovDir[x][y] =
7242           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7243         Moving2Blocked(x, y, &newx, &newy);
7244
7245         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7246           return;
7247
7248         MovDir[x][y] =
7249           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7250         Moving2Blocked(x, y, &newx, &newy);
7251
7252         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7253           return;
7254
7255         MovDir[x][y] = old_move_dir;
7256         return;
7257       }
7258     }
7259     else if (element == EL_EMC_ANDROID)
7260     {
7261       static int check_pos[16] =
7262       {
7263         -1,             //  0 => (invalid)
7264         7,              //  1 => MV_LEFT
7265         3,              //  2 => MV_RIGHT
7266         -1,             //  3 => (invalid)
7267         1,              //  4 =>            MV_UP
7268         0,              //  5 => MV_LEFT  | MV_UP
7269         2,              //  6 => MV_RIGHT | MV_UP
7270         -1,             //  7 => (invalid)
7271         5,              //  8 =>            MV_DOWN
7272         6,              //  9 => MV_LEFT  | MV_DOWN
7273         4,              // 10 => MV_RIGHT | MV_DOWN
7274         -1,             // 11 => (invalid)
7275         -1,             // 12 => (invalid)
7276         -1,             // 13 => (invalid)
7277         -1,             // 14 => (invalid)
7278         -1,             // 15 => (invalid)
7279       };
7280       static struct
7281       {
7282         int dx, dy;
7283         int dir;
7284       } check_xy[8] =
7285       {
7286         { -1, -1,       MV_LEFT  | MV_UP   },
7287         {  0, -1,                  MV_UP   },
7288         { +1, -1,       MV_RIGHT | MV_UP   },
7289         { +1,  0,       MV_RIGHT           },
7290         { +1, +1,       MV_RIGHT | MV_DOWN },
7291         {  0, +1,                  MV_DOWN },
7292         { -1, +1,       MV_LEFT  | MV_DOWN },
7293         { -1,  0,       MV_LEFT            },
7294       };
7295       int start_pos, check_order;
7296       boolean can_clone = FALSE;
7297       int i;
7298
7299       // check if there is any free field around current position
7300       for (i = 0; i < 8; i++)
7301       {
7302         int newx = x + check_xy[i].dx;
7303         int newy = y + check_xy[i].dy;
7304
7305         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7306         {
7307           can_clone = TRUE;
7308
7309           break;
7310         }
7311       }
7312
7313       if (can_clone)            // randomly find an element to clone
7314       {
7315         can_clone = FALSE;
7316
7317         start_pos = check_pos[RND(8)];
7318         check_order = (RND(2) ? -1 : +1);
7319
7320         for (i = 0; i < 8; i++)
7321         {
7322           int pos_raw = start_pos + i * check_order;
7323           int pos = (pos_raw + 8) % 8;
7324           int newx = x + check_xy[pos].dx;
7325           int newy = y + check_xy[pos].dy;
7326
7327           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7328           {
7329             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7330             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7331
7332             Store[x][y] = Tile[newx][newy];
7333
7334             can_clone = TRUE;
7335
7336             break;
7337           }
7338         }
7339       }
7340
7341       if (can_clone)            // randomly find a direction to move
7342       {
7343         can_clone = FALSE;
7344
7345         start_pos = check_pos[RND(8)];
7346         check_order = (RND(2) ? -1 : +1);
7347
7348         for (i = 0; i < 8; i++)
7349         {
7350           int pos_raw = start_pos + i * check_order;
7351           int pos = (pos_raw + 8) % 8;
7352           int newx = x + check_xy[pos].dx;
7353           int newy = y + check_xy[pos].dy;
7354           int new_move_dir = check_xy[pos].dir;
7355
7356           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7357           {
7358             MovDir[x][y] = new_move_dir;
7359             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7360
7361             can_clone = TRUE;
7362
7363             break;
7364           }
7365         }
7366       }
7367
7368       if (can_clone)            // cloning and moving successful
7369         return;
7370
7371       // cannot clone -- try to move towards player
7372
7373       start_pos = check_pos[MovDir[x][y] & 0x0f];
7374       check_order = (RND(2) ? -1 : +1);
7375
7376       for (i = 0; i < 3; i++)
7377       {
7378         // first check start_pos, then previous/next or (next/previous) pos
7379         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7380         int pos = (pos_raw + 8) % 8;
7381         int newx = x + check_xy[pos].dx;
7382         int newy = y + check_xy[pos].dy;
7383         int new_move_dir = check_xy[pos].dir;
7384
7385         if (IS_PLAYER(newx, newy))
7386           break;
7387
7388         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7389         {
7390           MovDir[x][y] = new_move_dir;
7391           MovDelay[x][y] = level.android_move_time * 8 + 1;
7392
7393           break;
7394         }
7395       }
7396     }
7397   }
7398   else if (move_pattern == MV_TURNING_LEFT ||
7399            move_pattern == MV_TURNING_RIGHT ||
7400            move_pattern == MV_TURNING_LEFT_RIGHT ||
7401            move_pattern == MV_TURNING_RIGHT_LEFT ||
7402            move_pattern == MV_TURNING_RANDOM ||
7403            move_pattern == MV_ALL_DIRECTIONS)
7404   {
7405     boolean can_turn_left =
7406       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7407     boolean can_turn_right =
7408       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7409
7410     if (element_info[element].move_stepsize == 0)       // "not moving"
7411       return;
7412
7413     if (move_pattern == MV_TURNING_LEFT)
7414       MovDir[x][y] = left_dir;
7415     else if (move_pattern == MV_TURNING_RIGHT)
7416       MovDir[x][y] = right_dir;
7417     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7418       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7419     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7420       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7421     else if (move_pattern == MV_TURNING_RANDOM)
7422       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7423                       can_turn_right && !can_turn_left ? right_dir :
7424                       RND(2) ? left_dir : right_dir);
7425     else if (can_turn_left && can_turn_right)
7426       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7427     else if (can_turn_left)
7428       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7429     else if (can_turn_right)
7430       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7431     else
7432       MovDir[x][y] = back_dir;
7433
7434     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7435   }
7436   else if (move_pattern == MV_HORIZONTAL ||
7437            move_pattern == MV_VERTICAL)
7438   {
7439     if (move_pattern & old_move_dir)
7440       MovDir[x][y] = back_dir;
7441     else if (move_pattern == MV_HORIZONTAL)
7442       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7443     else if (move_pattern == MV_VERTICAL)
7444       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7445
7446     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7447   }
7448   else if (move_pattern & MV_ANY_DIRECTION)
7449   {
7450     MovDir[x][y] = move_pattern;
7451     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7452   }
7453   else if (move_pattern & MV_WIND_DIRECTION)
7454   {
7455     MovDir[x][y] = game.wind_direction;
7456     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7457   }
7458   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7459   {
7460     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7461       MovDir[x][y] = left_dir;
7462     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7463       MovDir[x][y] = right_dir;
7464
7465     if (MovDir[x][y] != old_move_dir)
7466       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7467   }
7468   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7469   {
7470     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7471       MovDir[x][y] = right_dir;
7472     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7473       MovDir[x][y] = left_dir;
7474
7475     if (MovDir[x][y] != old_move_dir)
7476       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7477   }
7478   else if (move_pattern == MV_TOWARDS_PLAYER ||
7479            move_pattern == MV_AWAY_FROM_PLAYER)
7480   {
7481     int attr_x = -1, attr_y = -1;
7482     int newx, newy;
7483     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7484
7485     if (game.all_players_gone)
7486     {
7487       attr_x = game.exit_x;
7488       attr_y = game.exit_y;
7489     }
7490     else
7491     {
7492       int i;
7493
7494       for (i = 0; i < MAX_PLAYERS; i++)
7495       {
7496         struct PlayerInfo *player = &stored_player[i];
7497         int jx = player->jx, jy = player->jy;
7498
7499         if (!player->active)
7500           continue;
7501
7502         if (attr_x == -1 ||
7503             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7504         {
7505           attr_x = jx;
7506           attr_y = jy;
7507         }
7508       }
7509     }
7510
7511     MovDir[x][y] = MV_NONE;
7512     if (attr_x < x)
7513       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7514     else if (attr_x > x)
7515       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7516     if (attr_y < y)
7517       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7518     else if (attr_y > y)
7519       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7520
7521     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7522
7523     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7524     {
7525       boolean first_horiz = RND(2);
7526       int new_move_dir = MovDir[x][y];
7527
7528       if (element_info[element].move_stepsize == 0)     // "not moving"
7529       {
7530         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7531         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7532
7533         return;
7534       }
7535
7536       MovDir[x][y] =
7537         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7538       Moving2Blocked(x, y, &newx, &newy);
7539
7540       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7541         return;
7542
7543       MovDir[x][y] =
7544         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7545       Moving2Blocked(x, y, &newx, &newy);
7546
7547       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7548         return;
7549
7550       MovDir[x][y] = old_move_dir;
7551     }
7552   }
7553   else if (move_pattern == MV_WHEN_PUSHED ||
7554            move_pattern == MV_WHEN_DROPPED)
7555   {
7556     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7557       MovDir[x][y] = MV_NONE;
7558
7559     MovDelay[x][y] = 0;
7560   }
7561   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7562   {
7563     static int test_xy[7][2] =
7564     {
7565       { 0, -1 },
7566       { -1, 0 },
7567       { +1, 0 },
7568       { 0, +1 },
7569       { 0, -1 },
7570       { -1, 0 },
7571       { +1, 0 },
7572     };
7573     static int test_dir[7] =
7574     {
7575       MV_UP,
7576       MV_LEFT,
7577       MV_RIGHT,
7578       MV_DOWN,
7579       MV_UP,
7580       MV_LEFT,
7581       MV_RIGHT,
7582     };
7583     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7584     int move_preference = -1000000;     // start with very low preference
7585     int new_move_dir = MV_NONE;
7586     int start_test = RND(4);
7587     int i;
7588
7589     for (i = 0; i < NUM_DIRECTIONS; i++)
7590     {
7591       int move_dir = test_dir[start_test + i];
7592       int move_dir_preference;
7593
7594       xx = x + test_xy[start_test + i][0];
7595       yy = y + test_xy[start_test + i][1];
7596
7597       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7598           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7599       {
7600         new_move_dir = move_dir;
7601
7602         break;
7603       }
7604
7605       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7606         continue;
7607
7608       move_dir_preference = -1 * RunnerVisit[xx][yy];
7609       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7610         move_dir_preference = PlayerVisit[xx][yy];
7611
7612       if (move_dir_preference > move_preference)
7613       {
7614         // prefer field that has not been visited for the longest time
7615         move_preference = move_dir_preference;
7616         new_move_dir = move_dir;
7617       }
7618       else if (move_dir_preference == move_preference &&
7619                move_dir == old_move_dir)
7620       {
7621         // prefer last direction when all directions are preferred equally
7622         move_preference = move_dir_preference;
7623         new_move_dir = move_dir;
7624       }
7625     }
7626
7627     MovDir[x][y] = new_move_dir;
7628     if (old_move_dir != new_move_dir)
7629       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7630   }
7631 }
7632
7633 static void TurnRound(int x, int y)
7634 {
7635   int direction = MovDir[x][y];
7636
7637   TurnRoundExt(x, y);
7638
7639   GfxDir[x][y] = MovDir[x][y];
7640
7641   if (direction != MovDir[x][y])
7642     GfxFrame[x][y] = 0;
7643
7644   if (MovDelay[x][y])
7645     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7646
7647   ResetGfxFrame(x, y);
7648 }
7649
7650 static boolean JustBeingPushed(int x, int y)
7651 {
7652   int i;
7653
7654   for (i = 0; i < MAX_PLAYERS; i++)
7655   {
7656     struct PlayerInfo *player = &stored_player[i];
7657
7658     if (player->active && player->is_pushing && player->MovPos)
7659     {
7660       int next_jx = player->jx + (player->jx - player->last_jx);
7661       int next_jy = player->jy + (player->jy - player->last_jy);
7662
7663       if (x == next_jx && y == next_jy)
7664         return TRUE;
7665     }
7666   }
7667
7668   return FALSE;
7669 }
7670
7671 static void StartMoving(int x, int y)
7672 {
7673   boolean started_moving = FALSE;       // some elements can fall _and_ move
7674   int element = Tile[x][y];
7675
7676   if (Stop[x][y])
7677     return;
7678
7679   if (MovDelay[x][y] == 0)
7680     GfxAction[x][y] = ACTION_DEFAULT;
7681
7682   if (CAN_FALL(element) && y < lev_fieldy - 1)
7683   {
7684     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7685         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7686       if (JustBeingPushed(x, y))
7687         return;
7688
7689     if (element == EL_QUICKSAND_FULL)
7690     {
7691       if (IS_FREE(x, y + 1))
7692       {
7693         InitMovingField(x, y, MV_DOWN);
7694         started_moving = TRUE;
7695
7696         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7697 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7698         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7699           Store[x][y] = EL_ROCK;
7700 #else
7701         Store[x][y] = EL_ROCK;
7702 #endif
7703
7704         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7705       }
7706       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7707       {
7708         if (!MovDelay[x][y])
7709         {
7710           MovDelay[x][y] = TILEY + 1;
7711
7712           ResetGfxAnimation(x, y);
7713           ResetGfxAnimation(x, y + 1);
7714         }
7715
7716         if (MovDelay[x][y])
7717         {
7718           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7719           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7720
7721           MovDelay[x][y]--;
7722           if (MovDelay[x][y])
7723             return;
7724         }
7725
7726         Tile[x][y] = EL_QUICKSAND_EMPTY;
7727         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7728         Store[x][y + 1] = Store[x][y];
7729         Store[x][y] = 0;
7730
7731         PlayLevelSoundAction(x, y, ACTION_FILLING);
7732       }
7733       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7734       {
7735         if (!MovDelay[x][y])
7736         {
7737           MovDelay[x][y] = TILEY + 1;
7738
7739           ResetGfxAnimation(x, y);
7740           ResetGfxAnimation(x, y + 1);
7741         }
7742
7743         if (MovDelay[x][y])
7744         {
7745           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7746           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7747
7748           MovDelay[x][y]--;
7749           if (MovDelay[x][y])
7750             return;
7751         }
7752
7753         Tile[x][y] = EL_QUICKSAND_EMPTY;
7754         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7755         Store[x][y + 1] = Store[x][y];
7756         Store[x][y] = 0;
7757
7758         PlayLevelSoundAction(x, y, ACTION_FILLING);
7759       }
7760     }
7761     else if (element == EL_QUICKSAND_FAST_FULL)
7762     {
7763       if (IS_FREE(x, y + 1))
7764       {
7765         InitMovingField(x, y, MV_DOWN);
7766         started_moving = TRUE;
7767
7768         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7769 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7770         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7771           Store[x][y] = EL_ROCK;
7772 #else
7773         Store[x][y] = EL_ROCK;
7774 #endif
7775
7776         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7777       }
7778       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7779       {
7780         if (!MovDelay[x][y])
7781         {
7782           MovDelay[x][y] = TILEY + 1;
7783
7784           ResetGfxAnimation(x, y);
7785           ResetGfxAnimation(x, y + 1);
7786         }
7787
7788         if (MovDelay[x][y])
7789         {
7790           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7791           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7792
7793           MovDelay[x][y]--;
7794           if (MovDelay[x][y])
7795             return;
7796         }
7797
7798         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7799         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7800         Store[x][y + 1] = Store[x][y];
7801         Store[x][y] = 0;
7802
7803         PlayLevelSoundAction(x, y, ACTION_FILLING);
7804       }
7805       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7806       {
7807         if (!MovDelay[x][y])
7808         {
7809           MovDelay[x][y] = TILEY + 1;
7810
7811           ResetGfxAnimation(x, y);
7812           ResetGfxAnimation(x, y + 1);
7813         }
7814
7815         if (MovDelay[x][y])
7816         {
7817           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7818           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7819
7820           MovDelay[x][y]--;
7821           if (MovDelay[x][y])
7822             return;
7823         }
7824
7825         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7826         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7827         Store[x][y + 1] = Store[x][y];
7828         Store[x][y] = 0;
7829
7830         PlayLevelSoundAction(x, y, ACTION_FILLING);
7831       }
7832     }
7833     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7834              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7835     {
7836       InitMovingField(x, y, MV_DOWN);
7837       started_moving = TRUE;
7838
7839       Tile[x][y] = EL_QUICKSAND_FILLING;
7840       Store[x][y] = element;
7841
7842       PlayLevelSoundAction(x, y, ACTION_FILLING);
7843     }
7844     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7845              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7846     {
7847       InitMovingField(x, y, MV_DOWN);
7848       started_moving = TRUE;
7849
7850       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7851       Store[x][y] = element;
7852
7853       PlayLevelSoundAction(x, y, ACTION_FILLING);
7854     }
7855     else if (element == EL_MAGIC_WALL_FULL)
7856     {
7857       if (IS_FREE(x, y + 1))
7858       {
7859         InitMovingField(x, y, MV_DOWN);
7860         started_moving = TRUE;
7861
7862         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7863         Store[x][y] = EL_CHANGED(Store[x][y]);
7864       }
7865       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7866       {
7867         if (!MovDelay[x][y])
7868           MovDelay[x][y] = TILEY / 4 + 1;
7869
7870         if (MovDelay[x][y])
7871         {
7872           MovDelay[x][y]--;
7873           if (MovDelay[x][y])
7874             return;
7875         }
7876
7877         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7878         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7879         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7880         Store[x][y] = 0;
7881       }
7882     }
7883     else if (element == EL_BD_MAGIC_WALL_FULL)
7884     {
7885       if (IS_FREE(x, y + 1))
7886       {
7887         InitMovingField(x, y, MV_DOWN);
7888         started_moving = TRUE;
7889
7890         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7891         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7892       }
7893       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7894       {
7895         if (!MovDelay[x][y])
7896           MovDelay[x][y] = TILEY / 4 + 1;
7897
7898         if (MovDelay[x][y])
7899         {
7900           MovDelay[x][y]--;
7901           if (MovDelay[x][y])
7902             return;
7903         }
7904
7905         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7906         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7907         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7908         Store[x][y] = 0;
7909       }
7910     }
7911     else if (element == EL_DC_MAGIC_WALL_FULL)
7912     {
7913       if (IS_FREE(x, y + 1))
7914       {
7915         InitMovingField(x, y, MV_DOWN);
7916         started_moving = TRUE;
7917
7918         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7919         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7920       }
7921       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7922       {
7923         if (!MovDelay[x][y])
7924           MovDelay[x][y] = TILEY / 4 + 1;
7925
7926         if (MovDelay[x][y])
7927         {
7928           MovDelay[x][y]--;
7929           if (MovDelay[x][y])
7930             return;
7931         }
7932
7933         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7934         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7935         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7936         Store[x][y] = 0;
7937       }
7938     }
7939     else if ((CAN_PASS_MAGIC_WALL(element) &&
7940               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7941                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7942              (CAN_PASS_DC_MAGIC_WALL(element) &&
7943               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7944
7945     {
7946       InitMovingField(x, y, MV_DOWN);
7947       started_moving = TRUE;
7948
7949       Tile[x][y] =
7950         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7951          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7952          EL_DC_MAGIC_WALL_FILLING);
7953       Store[x][y] = element;
7954     }
7955     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7956     {
7957       SplashAcid(x, y + 1);
7958
7959       InitMovingField(x, y, MV_DOWN);
7960       started_moving = TRUE;
7961
7962       Store[x][y] = EL_ACID;
7963     }
7964     else if (
7965              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7966               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7967              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7968               CAN_FALL(element) && WasJustFalling[x][y] &&
7969               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7970
7971              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7972               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7973               (Tile[x][y + 1] == EL_BLOCKED)))
7974     {
7975       /* this is needed for a special case not covered by calling "Impact()"
7976          from "ContinueMoving()": if an element moves to a tile directly below
7977          another element which was just falling on that tile (which was empty
7978          in the previous frame), the falling element above would just stop
7979          instead of smashing the element below (in previous version, the above
7980          element was just checked for "moving" instead of "falling", resulting
7981          in incorrect smashes caused by horizontal movement of the above
7982          element; also, the case of the player being the element to smash was
7983          simply not covered here... :-/ ) */
7984
7985       CheckCollision[x][y] = 0;
7986       CheckImpact[x][y] = 0;
7987
7988       Impact(x, y);
7989     }
7990     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7991     {
7992       if (MovDir[x][y] == MV_NONE)
7993       {
7994         InitMovingField(x, y, MV_DOWN);
7995         started_moving = TRUE;
7996       }
7997     }
7998     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7999     {
8000       if (WasJustFalling[x][y]) // prevent animation from being restarted
8001         MovDir[x][y] = MV_DOWN;
8002
8003       InitMovingField(x, y, MV_DOWN);
8004       started_moving = TRUE;
8005     }
8006     else if (element == EL_AMOEBA_DROP)
8007     {
8008       Tile[x][y] = EL_AMOEBA_GROWING;
8009       Store[x][y] = EL_AMOEBA_WET;
8010     }
8011     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8012               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8013              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8014              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8015     {
8016       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8017                                 (IS_FREE(x - 1, y + 1) ||
8018                                  Tile[x - 1][y + 1] == EL_ACID));
8019       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8020                                 (IS_FREE(x + 1, y + 1) ||
8021                                  Tile[x + 1][y + 1] == EL_ACID));
8022       boolean can_fall_any  = (can_fall_left || can_fall_right);
8023       boolean can_fall_both = (can_fall_left && can_fall_right);
8024       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8025
8026       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8027       {
8028         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8029           can_fall_right = FALSE;
8030         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8031           can_fall_left = FALSE;
8032         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8033           can_fall_right = FALSE;
8034         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8035           can_fall_left = FALSE;
8036
8037         can_fall_any  = (can_fall_left || can_fall_right);
8038         can_fall_both = FALSE;
8039       }
8040
8041       if (can_fall_both)
8042       {
8043         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8044           can_fall_right = FALSE;       // slip down on left side
8045         else
8046           can_fall_left = !(can_fall_right = RND(2));
8047
8048         can_fall_both = FALSE;
8049       }
8050
8051       if (can_fall_any)
8052       {
8053         // if not determined otherwise, prefer left side for slipping down
8054         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8055         started_moving = TRUE;
8056       }
8057     }
8058     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8059     {
8060       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8061       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8062       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8063       int belt_dir = game.belt_dir[belt_nr];
8064
8065       if ((belt_dir == MV_LEFT  && left_is_free) ||
8066           (belt_dir == MV_RIGHT && right_is_free))
8067       {
8068         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8069
8070         InitMovingField(x, y, belt_dir);
8071         started_moving = TRUE;
8072
8073         Pushed[x][y] = TRUE;
8074         Pushed[nextx][y] = TRUE;
8075
8076         GfxAction[x][y] = ACTION_DEFAULT;
8077       }
8078       else
8079       {
8080         MovDir[x][y] = 0;       // if element was moving, stop it
8081       }
8082     }
8083   }
8084
8085   // not "else if" because of elements that can fall and move (EL_SPRING)
8086   if (CAN_MOVE(element) && !started_moving)
8087   {
8088     int move_pattern = element_info[element].move_pattern;
8089     int newx, newy;
8090
8091     Moving2Blocked(x, y, &newx, &newy);
8092
8093     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8094       return;
8095
8096     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8097         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8098     {
8099       WasJustMoving[x][y] = 0;
8100       CheckCollision[x][y] = 0;
8101
8102       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8103
8104       if (Tile[x][y] != element)        // element has changed
8105         return;
8106     }
8107
8108     if (!MovDelay[x][y])        // start new movement phase
8109     {
8110       // all objects that can change their move direction after each step
8111       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8112
8113       if (element != EL_YAMYAM &&
8114           element != EL_DARK_YAMYAM &&
8115           element != EL_PACMAN &&
8116           !(move_pattern & MV_ANY_DIRECTION) &&
8117           move_pattern != MV_TURNING_LEFT &&
8118           move_pattern != MV_TURNING_RIGHT &&
8119           move_pattern != MV_TURNING_LEFT_RIGHT &&
8120           move_pattern != MV_TURNING_RIGHT_LEFT &&
8121           move_pattern != MV_TURNING_RANDOM)
8122       {
8123         TurnRound(x, y);
8124
8125         if (MovDelay[x][y] && (element == EL_BUG ||
8126                                element == EL_SPACESHIP ||
8127                                element == EL_SP_SNIKSNAK ||
8128                                element == EL_SP_ELECTRON ||
8129                                element == EL_MOLE))
8130           TEST_DrawLevelField(x, y);
8131       }
8132     }
8133
8134     if (MovDelay[x][y])         // wait some time before next movement
8135     {
8136       MovDelay[x][y]--;
8137
8138       if (element == EL_ROBOT ||
8139           element == EL_YAMYAM ||
8140           element == EL_DARK_YAMYAM)
8141       {
8142         DrawLevelElementAnimationIfNeeded(x, y, element);
8143         PlayLevelSoundAction(x, y, ACTION_WAITING);
8144       }
8145       else if (element == EL_SP_ELECTRON)
8146         DrawLevelElementAnimationIfNeeded(x, y, element);
8147       else if (element == EL_DRAGON)
8148       {
8149         int i;
8150         int dir = MovDir[x][y];
8151         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8152         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8153         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8154                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8155                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8156                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8157         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8158
8159         GfxAction[x][y] = ACTION_ATTACKING;
8160
8161         if (IS_PLAYER(x, y))
8162           DrawPlayerField(x, y);
8163         else
8164           TEST_DrawLevelField(x, y);
8165
8166         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8167
8168         for (i = 1; i <= 3; i++)
8169         {
8170           int xx = x + i * dx;
8171           int yy = y + i * dy;
8172           int sx = SCREENX(xx);
8173           int sy = SCREENY(yy);
8174           int flame_graphic = graphic + (i - 1);
8175
8176           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8177             break;
8178
8179           if (MovDelay[x][y])
8180           {
8181             int flamed = MovingOrBlocked2Element(xx, yy);
8182
8183             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8184               Bang(xx, yy);
8185             else
8186               RemoveMovingField(xx, yy);
8187
8188             ChangeDelay[xx][yy] = 0;
8189
8190             Tile[xx][yy] = EL_FLAMES;
8191
8192             if (IN_SCR_FIELD(sx, sy))
8193             {
8194               TEST_DrawLevelFieldCrumbled(xx, yy);
8195               DrawGraphic(sx, sy, flame_graphic, frame);
8196             }
8197           }
8198           else
8199           {
8200             if (Tile[xx][yy] == EL_FLAMES)
8201               Tile[xx][yy] = EL_EMPTY;
8202             TEST_DrawLevelField(xx, yy);
8203           }
8204         }
8205       }
8206
8207       if (MovDelay[x][y])       // element still has to wait some time
8208       {
8209         PlayLevelSoundAction(x, y, ACTION_WAITING);
8210
8211         return;
8212       }
8213     }
8214
8215     // now make next step
8216
8217     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8218
8219     if (DONT_COLLIDE_WITH(element) &&
8220         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8221         !PLAYER_ENEMY_PROTECTED(newx, newy))
8222     {
8223       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8224
8225       return;
8226     }
8227
8228     else if (CAN_MOVE_INTO_ACID(element) &&
8229              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8230              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8231              (MovDir[x][y] == MV_DOWN ||
8232               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8233     {
8234       SplashAcid(newx, newy);
8235       Store[x][y] = EL_ACID;
8236     }
8237     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8238     {
8239       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8240           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8241           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8242           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8243       {
8244         RemoveField(x, y);
8245         TEST_DrawLevelField(x, y);
8246
8247         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8248         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8249           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8250
8251         game.friends_still_needed--;
8252         if (!game.friends_still_needed &&
8253             !game.GameOver &&
8254             game.all_players_gone)
8255           LevelSolved();
8256
8257         return;
8258       }
8259       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8260       {
8261         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8262           TEST_DrawLevelField(newx, newy);
8263         else
8264           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8265       }
8266       else if (!IS_FREE(newx, newy))
8267       {
8268         GfxAction[x][y] = ACTION_WAITING;
8269
8270         if (IS_PLAYER(x, y))
8271           DrawPlayerField(x, y);
8272         else
8273           TEST_DrawLevelField(x, y);
8274
8275         return;
8276       }
8277     }
8278     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8279     {
8280       if (IS_FOOD_PIG(Tile[newx][newy]))
8281       {
8282         if (IS_MOVING(newx, newy))
8283           RemoveMovingField(newx, newy);
8284         else
8285         {
8286           Tile[newx][newy] = EL_EMPTY;
8287           TEST_DrawLevelField(newx, newy);
8288         }
8289
8290         PlayLevelSound(x, y, SND_PIG_DIGGING);
8291       }
8292       else if (!IS_FREE(newx, newy))
8293       {
8294         if (IS_PLAYER(x, y))
8295           DrawPlayerField(x, y);
8296         else
8297           TEST_DrawLevelField(x, y);
8298
8299         return;
8300       }
8301     }
8302     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8303     {
8304       if (Store[x][y] != EL_EMPTY)
8305       {
8306         boolean can_clone = FALSE;
8307         int xx, yy;
8308
8309         // check if element to clone is still there
8310         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8311         {
8312           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8313           {
8314             can_clone = TRUE;
8315
8316             break;
8317           }
8318         }
8319
8320         // cannot clone or target field not free anymore -- do not clone
8321         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8322           Store[x][y] = EL_EMPTY;
8323       }
8324
8325       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8326       {
8327         if (IS_MV_DIAGONAL(MovDir[x][y]))
8328         {
8329           int diagonal_move_dir = MovDir[x][y];
8330           int stored = Store[x][y];
8331           int change_delay = 8;
8332           int graphic;
8333
8334           // android is moving diagonally
8335
8336           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8337
8338           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8339           GfxElement[x][y] = EL_EMC_ANDROID;
8340           GfxAction[x][y] = ACTION_SHRINKING;
8341           GfxDir[x][y] = diagonal_move_dir;
8342           ChangeDelay[x][y] = change_delay;
8343
8344           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8345                                    GfxDir[x][y]);
8346
8347           DrawLevelGraphicAnimation(x, y, graphic);
8348           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8349
8350           if (Tile[newx][newy] == EL_ACID)
8351           {
8352             SplashAcid(newx, newy);
8353
8354             return;
8355           }
8356
8357           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8358
8359           Store[newx][newy] = EL_EMC_ANDROID;
8360           GfxElement[newx][newy] = EL_EMC_ANDROID;
8361           GfxAction[newx][newy] = ACTION_GROWING;
8362           GfxDir[newx][newy] = diagonal_move_dir;
8363           ChangeDelay[newx][newy] = change_delay;
8364
8365           graphic = el_act_dir2img(GfxElement[newx][newy],
8366                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8367
8368           DrawLevelGraphicAnimation(newx, newy, graphic);
8369           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8370
8371           return;
8372         }
8373         else
8374         {
8375           Tile[newx][newy] = EL_EMPTY;
8376           TEST_DrawLevelField(newx, newy);
8377
8378           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8379         }
8380       }
8381       else if (!IS_FREE(newx, newy))
8382       {
8383         return;
8384       }
8385     }
8386     else if (IS_CUSTOM_ELEMENT(element) &&
8387              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8388     {
8389       if (!DigFieldByCE(newx, newy, element))
8390         return;
8391
8392       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8393       {
8394         RunnerVisit[x][y] = FrameCounter;
8395         PlayerVisit[x][y] /= 8;         // expire player visit path
8396       }
8397     }
8398     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8399     {
8400       if (!IS_FREE(newx, newy))
8401       {
8402         if (IS_PLAYER(x, y))
8403           DrawPlayerField(x, y);
8404         else
8405           TEST_DrawLevelField(x, y);
8406
8407         return;
8408       }
8409       else
8410       {
8411         boolean wanna_flame = !RND(10);
8412         int dx = newx - x, dy = newy - y;
8413         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8414         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8415         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8416                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8417         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8418                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8419
8420         if ((wanna_flame ||
8421              IS_CLASSIC_ENEMY(element1) ||
8422              IS_CLASSIC_ENEMY(element2)) &&
8423             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8424             element1 != EL_FLAMES && element2 != EL_FLAMES)
8425         {
8426           ResetGfxAnimation(x, y);
8427           GfxAction[x][y] = ACTION_ATTACKING;
8428
8429           if (IS_PLAYER(x, y))
8430             DrawPlayerField(x, y);
8431           else
8432             TEST_DrawLevelField(x, y);
8433
8434           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8435
8436           MovDelay[x][y] = 50;
8437
8438           Tile[newx][newy] = EL_FLAMES;
8439           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8440             Tile[newx1][newy1] = EL_FLAMES;
8441           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8442             Tile[newx2][newy2] = EL_FLAMES;
8443
8444           return;
8445         }
8446       }
8447     }
8448     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8449              Tile[newx][newy] == EL_DIAMOND)
8450     {
8451       if (IS_MOVING(newx, newy))
8452         RemoveMovingField(newx, newy);
8453       else
8454       {
8455         Tile[newx][newy] = EL_EMPTY;
8456         TEST_DrawLevelField(newx, newy);
8457       }
8458
8459       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8460     }
8461     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8462              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8463     {
8464       if (AmoebaNr[newx][newy])
8465       {
8466         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8467         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8468             Tile[newx][newy] == EL_BD_AMOEBA)
8469           AmoebaCnt[AmoebaNr[newx][newy]]--;
8470       }
8471
8472       if (IS_MOVING(newx, newy))
8473       {
8474         RemoveMovingField(newx, newy);
8475       }
8476       else
8477       {
8478         Tile[newx][newy] = EL_EMPTY;
8479         TEST_DrawLevelField(newx, newy);
8480       }
8481
8482       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8483     }
8484     else if ((element == EL_PACMAN || element == EL_MOLE)
8485              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8486     {
8487       if (AmoebaNr[newx][newy])
8488       {
8489         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8490         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8491             Tile[newx][newy] == EL_BD_AMOEBA)
8492           AmoebaCnt[AmoebaNr[newx][newy]]--;
8493       }
8494
8495       if (element == EL_MOLE)
8496       {
8497         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8498         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8499
8500         ResetGfxAnimation(x, y);
8501         GfxAction[x][y] = ACTION_DIGGING;
8502         TEST_DrawLevelField(x, y);
8503
8504         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8505
8506         return;                         // wait for shrinking amoeba
8507       }
8508       else      // element == EL_PACMAN
8509       {
8510         Tile[newx][newy] = EL_EMPTY;
8511         TEST_DrawLevelField(newx, newy);
8512         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8513       }
8514     }
8515     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8516              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8517               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8518     {
8519       // wait for shrinking amoeba to completely disappear
8520       return;
8521     }
8522     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8523     {
8524       // object was running against a wall
8525
8526       TurnRound(x, y);
8527
8528       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8529         DrawLevelElementAnimation(x, y, element);
8530
8531       if (DONT_TOUCH(element))
8532         TestIfBadThingTouchesPlayer(x, y);
8533
8534       return;
8535     }
8536
8537     InitMovingField(x, y, MovDir[x][y]);
8538
8539     PlayLevelSoundAction(x, y, ACTION_MOVING);
8540   }
8541
8542   if (MovDir[x][y])
8543     ContinueMoving(x, y);
8544 }
8545
8546 void ContinueMoving(int x, int y)
8547 {
8548   int element = Tile[x][y];
8549   struct ElementInfo *ei = &element_info[element];
8550   int direction = MovDir[x][y];
8551   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8552   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8553   int newx = x + dx, newy = y + dy;
8554   int stored = Store[x][y];
8555   int stored_new = Store[newx][newy];
8556   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8557   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8558   boolean last_line = (newy == lev_fieldy - 1);
8559   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8560
8561   if (pushed_by_player)         // special case: moving object pushed by player
8562   {
8563     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8564   }
8565   else if (use_step_delay)      // special case: moving object has step delay
8566   {
8567     if (!MovDelay[x][y])
8568       MovPos[x][y] += getElementMoveStepsize(x, y);
8569
8570     if (MovDelay[x][y])
8571       MovDelay[x][y]--;
8572     else
8573       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8574
8575     if (MovDelay[x][y])
8576     {
8577       TEST_DrawLevelField(x, y);
8578
8579       return;   // element is still waiting
8580     }
8581   }
8582   else                          // normal case: generically moving object
8583   {
8584     MovPos[x][y] += getElementMoveStepsize(x, y);
8585   }
8586
8587   if (ABS(MovPos[x][y]) < TILEX)
8588   {
8589     TEST_DrawLevelField(x, y);
8590
8591     return;     // element is still moving
8592   }
8593
8594   // element reached destination field
8595
8596   Tile[x][y] = EL_EMPTY;
8597   Tile[newx][newy] = element;
8598   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8599
8600   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8601   {
8602     element = Tile[newx][newy] = EL_ACID;
8603   }
8604   else if (element == EL_MOLE)
8605   {
8606     Tile[x][y] = EL_SAND;
8607
8608     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8609   }
8610   else if (element == EL_QUICKSAND_FILLING)
8611   {
8612     element = Tile[newx][newy] = get_next_element(element);
8613     Store[newx][newy] = Store[x][y];
8614   }
8615   else if (element == EL_QUICKSAND_EMPTYING)
8616   {
8617     Tile[x][y] = get_next_element(element);
8618     element = Tile[newx][newy] = Store[x][y];
8619   }
8620   else if (element == EL_QUICKSAND_FAST_FILLING)
8621   {
8622     element = Tile[newx][newy] = get_next_element(element);
8623     Store[newx][newy] = Store[x][y];
8624   }
8625   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8626   {
8627     Tile[x][y] = get_next_element(element);
8628     element = Tile[newx][newy] = Store[x][y];
8629   }
8630   else if (element == EL_MAGIC_WALL_FILLING)
8631   {
8632     element = Tile[newx][newy] = get_next_element(element);
8633     if (!game.magic_wall_active)
8634       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8635     Store[newx][newy] = Store[x][y];
8636   }
8637   else if (element == EL_MAGIC_WALL_EMPTYING)
8638   {
8639     Tile[x][y] = get_next_element(element);
8640     if (!game.magic_wall_active)
8641       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8642     element = Tile[newx][newy] = Store[x][y];
8643
8644     InitField(newx, newy, FALSE);
8645   }
8646   else if (element == EL_BD_MAGIC_WALL_FILLING)
8647   {
8648     element = Tile[newx][newy] = get_next_element(element);
8649     if (!game.magic_wall_active)
8650       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8651     Store[newx][newy] = Store[x][y];
8652   }
8653   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8654   {
8655     Tile[x][y] = get_next_element(element);
8656     if (!game.magic_wall_active)
8657       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8658     element = Tile[newx][newy] = Store[x][y];
8659
8660     InitField(newx, newy, FALSE);
8661   }
8662   else if (element == EL_DC_MAGIC_WALL_FILLING)
8663   {
8664     element = Tile[newx][newy] = get_next_element(element);
8665     if (!game.magic_wall_active)
8666       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8667     Store[newx][newy] = Store[x][y];
8668   }
8669   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8670   {
8671     Tile[x][y] = get_next_element(element);
8672     if (!game.magic_wall_active)
8673       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8674     element = Tile[newx][newy] = Store[x][y];
8675
8676     InitField(newx, newy, FALSE);
8677   }
8678   else if (element == EL_AMOEBA_DROPPING)
8679   {
8680     Tile[x][y] = get_next_element(element);
8681     element = Tile[newx][newy] = Store[x][y];
8682   }
8683   else if (element == EL_SOKOBAN_OBJECT)
8684   {
8685     if (Back[x][y])
8686       Tile[x][y] = Back[x][y];
8687
8688     if (Back[newx][newy])
8689       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8690
8691     Back[x][y] = Back[newx][newy] = 0;
8692   }
8693
8694   Store[x][y] = EL_EMPTY;
8695   MovPos[x][y] = 0;
8696   MovDir[x][y] = 0;
8697   MovDelay[x][y] = 0;
8698
8699   MovDelay[newx][newy] = 0;
8700
8701   if (CAN_CHANGE_OR_HAS_ACTION(element))
8702   {
8703     // copy element change control values to new field
8704     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8705     ChangePage[newx][newy]  = ChangePage[x][y];
8706     ChangeCount[newx][newy] = ChangeCount[x][y];
8707     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8708   }
8709
8710   CustomValue[newx][newy] = CustomValue[x][y];
8711
8712   ChangeDelay[x][y] = 0;
8713   ChangePage[x][y] = -1;
8714   ChangeCount[x][y] = 0;
8715   ChangeEvent[x][y] = -1;
8716
8717   CustomValue[x][y] = 0;
8718
8719   // copy animation control values to new field
8720   GfxFrame[newx][newy]  = GfxFrame[x][y];
8721   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8722   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8723   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8724
8725   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8726
8727   // some elements can leave other elements behind after moving
8728   if (ei->move_leave_element != EL_EMPTY &&
8729       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8730       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8731   {
8732     int move_leave_element = ei->move_leave_element;
8733
8734     // this makes it possible to leave the removed element again
8735     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8736       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8737
8738     Tile[x][y] = move_leave_element;
8739
8740     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8741       MovDir[x][y] = direction;
8742
8743     InitField(x, y, FALSE);
8744
8745     if (GFX_CRUMBLED(Tile[x][y]))
8746       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8747
8748     if (ELEM_IS_PLAYER(move_leave_element))
8749       RelocatePlayer(x, y, move_leave_element);
8750   }
8751
8752   // do this after checking for left-behind element
8753   ResetGfxAnimation(x, y);      // reset animation values for old field
8754
8755   if (!CAN_MOVE(element) ||
8756       (CAN_FALL(element) && direction == MV_DOWN &&
8757        (element == EL_SPRING ||
8758         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8759         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8760     GfxDir[x][y] = MovDir[newx][newy] = 0;
8761
8762   TEST_DrawLevelField(x, y);
8763   TEST_DrawLevelField(newx, newy);
8764
8765   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8766
8767   // prevent pushed element from moving on in pushed direction
8768   if (pushed_by_player && CAN_MOVE(element) &&
8769       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8770       !(element_info[element].move_pattern & direction))
8771     TurnRound(newx, newy);
8772
8773   // prevent elements on conveyor belt from moving on in last direction
8774   if (pushed_by_conveyor && CAN_FALL(element) &&
8775       direction & MV_HORIZONTAL)
8776     MovDir[newx][newy] = 0;
8777
8778   if (!pushed_by_player)
8779   {
8780     int nextx = newx + dx, nexty = newy + dy;
8781     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8782
8783     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8784
8785     if (CAN_FALL(element) && direction == MV_DOWN)
8786       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8787
8788     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8789       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8790
8791     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8792       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8793   }
8794
8795   if (DONT_TOUCH(element))      // object may be nasty to player or others
8796   {
8797     TestIfBadThingTouchesPlayer(newx, newy);
8798     TestIfBadThingTouchesFriend(newx, newy);
8799
8800     if (!IS_CUSTOM_ELEMENT(element))
8801       TestIfBadThingTouchesOtherBadThing(newx, newy);
8802   }
8803   else if (element == EL_PENGUIN)
8804     TestIfFriendTouchesBadThing(newx, newy);
8805
8806   if (DONT_GET_HIT_BY(element))
8807   {
8808     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8809   }
8810
8811   // give the player one last chance (one more frame) to move away
8812   if (CAN_FALL(element) && direction == MV_DOWN &&
8813       (last_line || (!IS_FREE(x, newy + 1) &&
8814                      (!IS_PLAYER(x, newy + 1) ||
8815                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8816     Impact(x, newy);
8817
8818   if (pushed_by_player && !game.use_change_when_pushing_bug)
8819   {
8820     int push_side = MV_DIR_OPPOSITE(direction);
8821     struct PlayerInfo *player = PLAYERINFO(x, y);
8822
8823     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8824                                player->index_bit, push_side);
8825     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8826                                         player->index_bit, push_side);
8827   }
8828
8829   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8830     MovDelay[newx][newy] = 1;
8831
8832   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8833
8834   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8835   TestIfElementHitsCustomElement(newx, newy, direction);
8836   TestIfPlayerTouchesCustomElement(newx, newy);
8837   TestIfElementTouchesCustomElement(newx, newy);
8838
8839   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8840       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8841     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8842                              MV_DIR_OPPOSITE(direction));
8843 }
8844
8845 int AmoebaNeighbourNr(int ax, int ay)
8846 {
8847   int i;
8848   int element = Tile[ax][ay];
8849   int group_nr = 0;
8850   static int xy[4][2] =
8851   {
8852     { 0, -1 },
8853     { -1, 0 },
8854     { +1, 0 },
8855     { 0, +1 }
8856   };
8857
8858   for (i = 0; i < NUM_DIRECTIONS; i++)
8859   {
8860     int x = ax + xy[i][0];
8861     int y = ay + xy[i][1];
8862
8863     if (!IN_LEV_FIELD(x, y))
8864       continue;
8865
8866     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8867       group_nr = AmoebaNr[x][y];
8868   }
8869
8870   return group_nr;
8871 }
8872
8873 static void AmoebaMerge(int ax, int ay)
8874 {
8875   int i, x, y, xx, yy;
8876   int new_group_nr = AmoebaNr[ax][ay];
8877   static int xy[4][2] =
8878   {
8879     { 0, -1 },
8880     { -1, 0 },
8881     { +1, 0 },
8882     { 0, +1 }
8883   };
8884
8885   if (new_group_nr == 0)
8886     return;
8887
8888   for (i = 0; i < NUM_DIRECTIONS; i++)
8889   {
8890     x = ax + xy[i][0];
8891     y = ay + xy[i][1];
8892
8893     if (!IN_LEV_FIELD(x, y))
8894       continue;
8895
8896     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8897          Tile[x][y] == EL_BD_AMOEBA ||
8898          Tile[x][y] == EL_AMOEBA_DEAD) &&
8899         AmoebaNr[x][y] != new_group_nr)
8900     {
8901       int old_group_nr = AmoebaNr[x][y];
8902
8903       if (old_group_nr == 0)
8904         return;
8905
8906       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8907       AmoebaCnt[old_group_nr] = 0;
8908       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8909       AmoebaCnt2[old_group_nr] = 0;
8910
8911       SCAN_PLAYFIELD(xx, yy)
8912       {
8913         if (AmoebaNr[xx][yy] == old_group_nr)
8914           AmoebaNr[xx][yy] = new_group_nr;
8915       }
8916     }
8917   }
8918 }
8919
8920 void AmoebaToDiamond(int ax, int ay)
8921 {
8922   int i, x, y;
8923
8924   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8925   {
8926     int group_nr = AmoebaNr[ax][ay];
8927
8928 #ifdef DEBUG
8929     if (group_nr == 0)
8930     {
8931       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8932       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8933
8934       return;
8935     }
8936 #endif
8937
8938     SCAN_PLAYFIELD(x, y)
8939     {
8940       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8941       {
8942         AmoebaNr[x][y] = 0;
8943         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8944       }
8945     }
8946
8947     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8948                             SND_AMOEBA_TURNING_TO_GEM :
8949                             SND_AMOEBA_TURNING_TO_ROCK));
8950     Bang(ax, ay);
8951   }
8952   else
8953   {
8954     static int xy[4][2] =
8955     {
8956       { 0, -1 },
8957       { -1, 0 },
8958       { +1, 0 },
8959       { 0, +1 }
8960     };
8961
8962     for (i = 0; i < NUM_DIRECTIONS; i++)
8963     {
8964       x = ax + xy[i][0];
8965       y = ay + xy[i][1];
8966
8967       if (!IN_LEV_FIELD(x, y))
8968         continue;
8969
8970       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8971       {
8972         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8973                               SND_AMOEBA_TURNING_TO_GEM :
8974                               SND_AMOEBA_TURNING_TO_ROCK));
8975         Bang(x, y);
8976       }
8977     }
8978   }
8979 }
8980
8981 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8982 {
8983   int x, y;
8984   int group_nr = AmoebaNr[ax][ay];
8985   boolean done = FALSE;
8986
8987 #ifdef DEBUG
8988   if (group_nr == 0)
8989   {
8990     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8991     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8992
8993     return;
8994   }
8995 #endif
8996
8997   SCAN_PLAYFIELD(x, y)
8998   {
8999     if (AmoebaNr[x][y] == group_nr &&
9000         (Tile[x][y] == EL_AMOEBA_DEAD ||
9001          Tile[x][y] == EL_BD_AMOEBA ||
9002          Tile[x][y] == EL_AMOEBA_GROWING))
9003     {
9004       AmoebaNr[x][y] = 0;
9005       Tile[x][y] = new_element;
9006       InitField(x, y, FALSE);
9007       TEST_DrawLevelField(x, y);
9008       done = TRUE;
9009     }
9010   }
9011
9012   if (done)
9013     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9014                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9015                             SND_BD_AMOEBA_TURNING_TO_GEM));
9016 }
9017
9018 static void AmoebaGrowing(int x, int y)
9019 {
9020   static unsigned int sound_delay = 0;
9021   static unsigned int sound_delay_value = 0;
9022
9023   if (!MovDelay[x][y])          // start new growing cycle
9024   {
9025     MovDelay[x][y] = 7;
9026
9027     if (DelayReached(&sound_delay, sound_delay_value))
9028     {
9029       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9030       sound_delay_value = 30;
9031     }
9032   }
9033
9034   if (MovDelay[x][y])           // wait some time before growing bigger
9035   {
9036     MovDelay[x][y]--;
9037     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9038     {
9039       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9040                                            6 - MovDelay[x][y]);
9041
9042       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9043     }
9044
9045     if (!MovDelay[x][y])
9046     {
9047       Tile[x][y] = Store[x][y];
9048       Store[x][y] = 0;
9049       TEST_DrawLevelField(x, y);
9050     }
9051   }
9052 }
9053
9054 static void AmoebaShrinking(int x, int y)
9055 {
9056   static unsigned int sound_delay = 0;
9057   static unsigned int sound_delay_value = 0;
9058
9059   if (!MovDelay[x][y])          // start new shrinking cycle
9060   {
9061     MovDelay[x][y] = 7;
9062
9063     if (DelayReached(&sound_delay, sound_delay_value))
9064       sound_delay_value = 30;
9065   }
9066
9067   if (MovDelay[x][y])           // wait some time before shrinking
9068   {
9069     MovDelay[x][y]--;
9070     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9071     {
9072       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9073                                            6 - MovDelay[x][y]);
9074
9075       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9076     }
9077
9078     if (!MovDelay[x][y])
9079     {
9080       Tile[x][y] = EL_EMPTY;
9081       TEST_DrawLevelField(x, y);
9082
9083       // don't let mole enter this field in this cycle;
9084       // (give priority to objects falling to this field from above)
9085       Stop[x][y] = TRUE;
9086     }
9087   }
9088 }
9089
9090 static void AmoebaReproduce(int ax, int ay)
9091 {
9092   int i;
9093   int element = Tile[ax][ay];
9094   int graphic = el2img(element);
9095   int newax = ax, neway = ay;
9096   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9097   static int xy[4][2] =
9098   {
9099     { 0, -1 },
9100     { -1, 0 },
9101     { +1, 0 },
9102     { 0, +1 }
9103   };
9104
9105   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9106   {
9107     Tile[ax][ay] = EL_AMOEBA_DEAD;
9108     TEST_DrawLevelField(ax, ay);
9109     return;
9110   }
9111
9112   if (IS_ANIMATED(graphic))
9113     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9114
9115   if (!MovDelay[ax][ay])        // start making new amoeba field
9116     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9117
9118   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9119   {
9120     MovDelay[ax][ay]--;
9121     if (MovDelay[ax][ay])
9122       return;
9123   }
9124
9125   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9126   {
9127     int start = RND(4);
9128     int x = ax + xy[start][0];
9129     int y = ay + xy[start][1];
9130
9131     if (!IN_LEV_FIELD(x, y))
9132       return;
9133
9134     if (IS_FREE(x, y) ||
9135         CAN_GROW_INTO(Tile[x][y]) ||
9136         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9137         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9138     {
9139       newax = x;
9140       neway = y;
9141     }
9142
9143     if (newax == ax && neway == ay)
9144       return;
9145   }
9146   else                          // normal or "filled" (BD style) amoeba
9147   {
9148     int start = RND(4);
9149     boolean waiting_for_player = FALSE;
9150
9151     for (i = 0; i < NUM_DIRECTIONS; i++)
9152     {
9153       int j = (start + i) % 4;
9154       int x = ax + xy[j][0];
9155       int y = ay + xy[j][1];
9156
9157       if (!IN_LEV_FIELD(x, y))
9158         continue;
9159
9160       if (IS_FREE(x, y) ||
9161           CAN_GROW_INTO(Tile[x][y]) ||
9162           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9163           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9164       {
9165         newax = x;
9166         neway = y;
9167         break;
9168       }
9169       else if (IS_PLAYER(x, y))
9170         waiting_for_player = TRUE;
9171     }
9172
9173     if (newax == ax && neway == ay)             // amoeba cannot grow
9174     {
9175       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9176       {
9177         Tile[ax][ay] = EL_AMOEBA_DEAD;
9178         TEST_DrawLevelField(ax, ay);
9179         AmoebaCnt[AmoebaNr[ax][ay]]--;
9180
9181         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9182         {
9183           if (element == EL_AMOEBA_FULL)
9184             AmoebaToDiamond(ax, ay);
9185           else if (element == EL_BD_AMOEBA)
9186             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9187         }
9188       }
9189       return;
9190     }
9191     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9192     {
9193       // amoeba gets larger by growing in some direction
9194
9195       int new_group_nr = AmoebaNr[ax][ay];
9196
9197 #ifdef DEBUG
9198   if (new_group_nr == 0)
9199   {
9200     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9201           newax, neway);
9202     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9203
9204     return;
9205   }
9206 #endif
9207
9208       AmoebaNr[newax][neway] = new_group_nr;
9209       AmoebaCnt[new_group_nr]++;
9210       AmoebaCnt2[new_group_nr]++;
9211
9212       // if amoeba touches other amoeba(s) after growing, unify them
9213       AmoebaMerge(newax, neway);
9214
9215       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9216       {
9217         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9218         return;
9219       }
9220     }
9221   }
9222
9223   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9224       (neway == lev_fieldy - 1 && newax != ax))
9225   {
9226     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9227     Store[newax][neway] = element;
9228   }
9229   else if (neway == ay || element == EL_EMC_DRIPPER)
9230   {
9231     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9232
9233     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9234   }
9235   else
9236   {
9237     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9238     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9239     Store[ax][ay] = EL_AMOEBA_DROP;
9240     ContinueMoving(ax, ay);
9241     return;
9242   }
9243
9244   TEST_DrawLevelField(newax, neway);
9245 }
9246
9247 static void Life(int ax, int ay)
9248 {
9249   int x1, y1, x2, y2;
9250   int life_time = 40;
9251   int element = Tile[ax][ay];
9252   int graphic = el2img(element);
9253   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9254                          level.biomaze);
9255   boolean changed = FALSE;
9256
9257   if (IS_ANIMATED(graphic))
9258     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9259
9260   if (Stop[ax][ay])
9261     return;
9262
9263   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9264     MovDelay[ax][ay] = life_time;
9265
9266   if (MovDelay[ax][ay])         // wait some time before next cycle
9267   {
9268     MovDelay[ax][ay]--;
9269     if (MovDelay[ax][ay])
9270       return;
9271   }
9272
9273   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9274   {
9275     int xx = ax+x1, yy = ay+y1;
9276     int old_element = Tile[xx][yy];
9277     int num_neighbours = 0;
9278
9279     if (!IN_LEV_FIELD(xx, yy))
9280       continue;
9281
9282     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9283     {
9284       int x = xx+x2, y = yy+y2;
9285
9286       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9287         continue;
9288
9289       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9290       boolean is_neighbour = FALSE;
9291
9292       if (level.use_life_bugs)
9293         is_neighbour =
9294           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9295            (IS_FREE(x, y)                             &&  Stop[x][y]));
9296       else
9297         is_neighbour =
9298           (Last[x][y] == element || is_player_cell);
9299
9300       if (is_neighbour)
9301         num_neighbours++;
9302     }
9303
9304     boolean is_free = FALSE;
9305
9306     if (level.use_life_bugs)
9307       is_free = (IS_FREE(xx, yy));
9308     else
9309       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9310
9311     if (xx == ax && yy == ay)           // field in the middle
9312     {
9313       if (num_neighbours < life_parameter[0] ||
9314           num_neighbours > life_parameter[1])
9315       {
9316         Tile[xx][yy] = EL_EMPTY;
9317         if (Tile[xx][yy] != old_element)
9318           TEST_DrawLevelField(xx, yy);
9319         Stop[xx][yy] = TRUE;
9320         changed = TRUE;
9321       }
9322     }
9323     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9324     {                                   // free border field
9325       if (num_neighbours >= life_parameter[2] &&
9326           num_neighbours <= life_parameter[3])
9327       {
9328         Tile[xx][yy] = element;
9329         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9330         if (Tile[xx][yy] != old_element)
9331           TEST_DrawLevelField(xx, yy);
9332         Stop[xx][yy] = TRUE;
9333         changed = TRUE;
9334       }
9335     }
9336   }
9337
9338   if (changed)
9339     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9340                    SND_GAME_OF_LIFE_GROWING);
9341 }
9342
9343 static void InitRobotWheel(int x, int y)
9344 {
9345   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9346 }
9347
9348 static void RunRobotWheel(int x, int y)
9349 {
9350   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9351 }
9352
9353 static void StopRobotWheel(int x, int y)
9354 {
9355   if (game.robot_wheel_x == x &&
9356       game.robot_wheel_y == y)
9357   {
9358     game.robot_wheel_x = -1;
9359     game.robot_wheel_y = -1;
9360     game.robot_wheel_active = FALSE;
9361   }
9362 }
9363
9364 static void InitTimegateWheel(int x, int y)
9365 {
9366   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9367 }
9368
9369 static void RunTimegateWheel(int x, int y)
9370 {
9371   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9372 }
9373
9374 static void InitMagicBallDelay(int x, int y)
9375 {
9376   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9377 }
9378
9379 static void ActivateMagicBall(int bx, int by)
9380 {
9381   int x, y;
9382
9383   if (level.ball_random)
9384   {
9385     int pos_border = RND(8);    // select one of the eight border elements
9386     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9387     int xx = pos_content % 3;
9388     int yy = pos_content / 3;
9389
9390     x = bx - 1 + xx;
9391     y = by - 1 + yy;
9392
9393     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9394       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9395   }
9396   else
9397   {
9398     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9399     {
9400       int xx = x - bx + 1;
9401       int yy = y - by + 1;
9402
9403       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9404         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9405     }
9406   }
9407
9408   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9409 }
9410
9411 static void CheckExit(int x, int y)
9412 {
9413   if (game.gems_still_needed > 0 ||
9414       game.sokoban_fields_still_needed > 0 ||
9415       game.sokoban_objects_still_needed > 0 ||
9416       game.lights_still_needed > 0)
9417   {
9418     int element = Tile[x][y];
9419     int graphic = el2img(element);
9420
9421     if (IS_ANIMATED(graphic))
9422       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9423
9424     return;
9425   }
9426
9427   // do not re-open exit door closed after last player
9428   if (game.all_players_gone)
9429     return;
9430
9431   Tile[x][y] = EL_EXIT_OPENING;
9432
9433   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9434 }
9435
9436 static void CheckExitEM(int x, int y)
9437 {
9438   if (game.gems_still_needed > 0 ||
9439       game.sokoban_fields_still_needed > 0 ||
9440       game.sokoban_objects_still_needed > 0 ||
9441       game.lights_still_needed > 0)
9442   {
9443     int element = Tile[x][y];
9444     int graphic = el2img(element);
9445
9446     if (IS_ANIMATED(graphic))
9447       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9448
9449     return;
9450   }
9451
9452   // do not re-open exit door closed after last player
9453   if (game.all_players_gone)
9454     return;
9455
9456   Tile[x][y] = EL_EM_EXIT_OPENING;
9457
9458   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9459 }
9460
9461 static void CheckExitSteel(int x, int y)
9462 {
9463   if (game.gems_still_needed > 0 ||
9464       game.sokoban_fields_still_needed > 0 ||
9465       game.sokoban_objects_still_needed > 0 ||
9466       game.lights_still_needed > 0)
9467   {
9468     int element = Tile[x][y];
9469     int graphic = el2img(element);
9470
9471     if (IS_ANIMATED(graphic))
9472       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9473
9474     return;
9475   }
9476
9477   // do not re-open exit door closed after last player
9478   if (game.all_players_gone)
9479     return;
9480
9481   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9482
9483   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9484 }
9485
9486 static void CheckExitSteelEM(int x, int y)
9487 {
9488   if (game.gems_still_needed > 0 ||
9489       game.sokoban_fields_still_needed > 0 ||
9490       game.sokoban_objects_still_needed > 0 ||
9491       game.lights_still_needed > 0)
9492   {
9493     int element = Tile[x][y];
9494     int graphic = el2img(element);
9495
9496     if (IS_ANIMATED(graphic))
9497       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9498
9499     return;
9500   }
9501
9502   // do not re-open exit door closed after last player
9503   if (game.all_players_gone)
9504     return;
9505
9506   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9507
9508   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9509 }
9510
9511 static void CheckExitSP(int x, int y)
9512 {
9513   if (game.gems_still_needed > 0)
9514   {
9515     int element = Tile[x][y];
9516     int graphic = el2img(element);
9517
9518     if (IS_ANIMATED(graphic))
9519       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9520
9521     return;
9522   }
9523
9524   // do not re-open exit door closed after last player
9525   if (game.all_players_gone)
9526     return;
9527
9528   Tile[x][y] = EL_SP_EXIT_OPENING;
9529
9530   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9531 }
9532
9533 static void CloseAllOpenTimegates(void)
9534 {
9535   int x, y;
9536
9537   SCAN_PLAYFIELD(x, y)
9538   {
9539     int element = Tile[x][y];
9540
9541     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9542     {
9543       Tile[x][y] = EL_TIMEGATE_CLOSING;
9544
9545       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9546     }
9547   }
9548 }
9549
9550 static void DrawTwinkleOnField(int x, int y)
9551 {
9552   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9553     return;
9554
9555   if (Tile[x][y] == EL_BD_DIAMOND)
9556     return;
9557
9558   if (MovDelay[x][y] == 0)      // next animation frame
9559     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9560
9561   if (MovDelay[x][y] != 0)      // wait some time before next frame
9562   {
9563     MovDelay[x][y]--;
9564
9565     DrawLevelElementAnimation(x, y, Tile[x][y]);
9566
9567     if (MovDelay[x][y] != 0)
9568     {
9569       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9570                                            10 - MovDelay[x][y]);
9571
9572       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9573     }
9574   }
9575 }
9576
9577 static void MauerWaechst(int x, int y)
9578 {
9579   int delay = 6;
9580
9581   if (!MovDelay[x][y])          // next animation frame
9582     MovDelay[x][y] = 3 * delay;
9583
9584   if (MovDelay[x][y])           // wait some time before next frame
9585   {
9586     MovDelay[x][y]--;
9587
9588     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9589     {
9590       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9591       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9592
9593       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9594     }
9595
9596     if (!MovDelay[x][y])
9597     {
9598       if (MovDir[x][y] == MV_LEFT)
9599       {
9600         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9601           TEST_DrawLevelField(x - 1, y);
9602       }
9603       else if (MovDir[x][y] == MV_RIGHT)
9604       {
9605         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9606           TEST_DrawLevelField(x + 1, y);
9607       }
9608       else if (MovDir[x][y] == MV_UP)
9609       {
9610         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9611           TEST_DrawLevelField(x, y - 1);
9612       }
9613       else
9614       {
9615         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9616           TEST_DrawLevelField(x, y + 1);
9617       }
9618
9619       Tile[x][y] = Store[x][y];
9620       Store[x][y] = 0;
9621       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9622       TEST_DrawLevelField(x, y);
9623     }
9624   }
9625 }
9626
9627 static void MauerAbleger(int ax, int ay)
9628 {
9629   int element = Tile[ax][ay];
9630   int graphic = el2img(element);
9631   boolean oben_frei = FALSE, unten_frei = FALSE;
9632   boolean links_frei = FALSE, rechts_frei = FALSE;
9633   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9634   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9635   boolean new_wall = FALSE;
9636
9637   if (IS_ANIMATED(graphic))
9638     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9639
9640   if (!MovDelay[ax][ay])        // start building new wall
9641     MovDelay[ax][ay] = 6;
9642
9643   if (MovDelay[ax][ay])         // wait some time before building new wall
9644   {
9645     MovDelay[ax][ay]--;
9646     if (MovDelay[ax][ay])
9647       return;
9648   }
9649
9650   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9651     oben_frei = TRUE;
9652   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9653     unten_frei = TRUE;
9654   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9655     links_frei = TRUE;
9656   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9657     rechts_frei = TRUE;
9658
9659   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9660       element == EL_EXPANDABLE_WALL_ANY)
9661   {
9662     if (oben_frei)
9663     {
9664       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9665       Store[ax][ay-1] = element;
9666       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9667       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9668         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9669                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9670       new_wall = TRUE;
9671     }
9672     if (unten_frei)
9673     {
9674       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9675       Store[ax][ay+1] = element;
9676       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9677       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9678         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9679                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9680       new_wall = TRUE;
9681     }
9682   }
9683
9684   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9685       element == EL_EXPANDABLE_WALL_ANY ||
9686       element == EL_EXPANDABLE_WALL ||
9687       element == EL_BD_EXPANDABLE_WALL)
9688   {
9689     if (links_frei)
9690     {
9691       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9692       Store[ax-1][ay] = element;
9693       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9694       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9695         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9696                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9697       new_wall = TRUE;
9698     }
9699
9700     if (rechts_frei)
9701     {
9702       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9703       Store[ax+1][ay] = element;
9704       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9705       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9706         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9707                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9708       new_wall = TRUE;
9709     }
9710   }
9711
9712   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9713     TEST_DrawLevelField(ax, ay);
9714
9715   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9716     oben_massiv = TRUE;
9717   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9718     unten_massiv = TRUE;
9719   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9720     links_massiv = TRUE;
9721   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9722     rechts_massiv = TRUE;
9723
9724   if (((oben_massiv && unten_massiv) ||
9725        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9726        element == EL_EXPANDABLE_WALL) &&
9727       ((links_massiv && rechts_massiv) ||
9728        element == EL_EXPANDABLE_WALL_VERTICAL))
9729     Tile[ax][ay] = EL_WALL;
9730
9731   if (new_wall)
9732     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9733 }
9734
9735 static void MauerAblegerStahl(int ax, int ay)
9736 {
9737   int element = Tile[ax][ay];
9738   int graphic = el2img(element);
9739   boolean oben_frei = FALSE, unten_frei = FALSE;
9740   boolean links_frei = FALSE, rechts_frei = FALSE;
9741   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9742   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9743   boolean new_wall = FALSE;
9744
9745   if (IS_ANIMATED(graphic))
9746     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9747
9748   if (!MovDelay[ax][ay])        // start building new wall
9749     MovDelay[ax][ay] = 6;
9750
9751   if (MovDelay[ax][ay])         // wait some time before building new wall
9752   {
9753     MovDelay[ax][ay]--;
9754     if (MovDelay[ax][ay])
9755       return;
9756   }
9757
9758   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9759     oben_frei = TRUE;
9760   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9761     unten_frei = TRUE;
9762   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9763     links_frei = TRUE;
9764   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9765     rechts_frei = TRUE;
9766
9767   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9768       element == EL_EXPANDABLE_STEELWALL_ANY)
9769   {
9770     if (oben_frei)
9771     {
9772       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9773       Store[ax][ay-1] = element;
9774       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9775       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9776         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9777                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9778       new_wall = TRUE;
9779     }
9780     if (unten_frei)
9781     {
9782       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9783       Store[ax][ay+1] = element;
9784       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9785       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9786         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9787                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9788       new_wall = TRUE;
9789     }
9790   }
9791
9792   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9793       element == EL_EXPANDABLE_STEELWALL_ANY)
9794   {
9795     if (links_frei)
9796     {
9797       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9798       Store[ax-1][ay] = element;
9799       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9800       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9801         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9802                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9803       new_wall = TRUE;
9804     }
9805
9806     if (rechts_frei)
9807     {
9808       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9809       Store[ax+1][ay] = element;
9810       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9811       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9812         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9813                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9814       new_wall = TRUE;
9815     }
9816   }
9817
9818   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9819     oben_massiv = TRUE;
9820   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9821     unten_massiv = TRUE;
9822   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9823     links_massiv = TRUE;
9824   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9825     rechts_massiv = TRUE;
9826
9827   if (((oben_massiv && unten_massiv) ||
9828        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9829       ((links_massiv && rechts_massiv) ||
9830        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9831     Tile[ax][ay] = EL_STEELWALL;
9832
9833   if (new_wall)
9834     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9835 }
9836
9837 static void CheckForDragon(int x, int y)
9838 {
9839   int i, j;
9840   boolean dragon_found = FALSE;
9841   static int xy[4][2] =
9842   {
9843     { 0, -1 },
9844     { -1, 0 },
9845     { +1, 0 },
9846     { 0, +1 }
9847   };
9848
9849   for (i = 0; i < NUM_DIRECTIONS; i++)
9850   {
9851     for (j = 0; j < 4; j++)
9852     {
9853       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9854
9855       if (IN_LEV_FIELD(xx, yy) &&
9856           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9857       {
9858         if (Tile[xx][yy] == EL_DRAGON)
9859           dragon_found = TRUE;
9860       }
9861       else
9862         break;
9863     }
9864   }
9865
9866   if (!dragon_found)
9867   {
9868     for (i = 0; i < NUM_DIRECTIONS; i++)
9869     {
9870       for (j = 0; j < 3; j++)
9871       {
9872         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9873   
9874         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9875         {
9876           Tile[xx][yy] = EL_EMPTY;
9877           TEST_DrawLevelField(xx, yy);
9878         }
9879         else
9880           break;
9881       }
9882     }
9883   }
9884 }
9885
9886 static void InitBuggyBase(int x, int y)
9887 {
9888   int element = Tile[x][y];
9889   int activating_delay = FRAMES_PER_SECOND / 4;
9890
9891   ChangeDelay[x][y] =
9892     (element == EL_SP_BUGGY_BASE ?
9893      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9894      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9895      activating_delay :
9896      element == EL_SP_BUGGY_BASE_ACTIVE ?
9897      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9898 }
9899
9900 static void WarnBuggyBase(int x, int y)
9901 {
9902   int i;
9903   static int xy[4][2] =
9904   {
9905     { 0, -1 },
9906     { -1, 0 },
9907     { +1, 0 },
9908     { 0, +1 }
9909   };
9910
9911   for (i = 0; i < NUM_DIRECTIONS; i++)
9912   {
9913     int xx = x + xy[i][0];
9914     int yy = y + xy[i][1];
9915
9916     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9917     {
9918       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9919
9920       break;
9921     }
9922   }
9923 }
9924
9925 static void InitTrap(int x, int y)
9926 {
9927   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9928 }
9929
9930 static void ActivateTrap(int x, int y)
9931 {
9932   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9933 }
9934
9935 static void ChangeActiveTrap(int x, int y)
9936 {
9937   int graphic = IMG_TRAP_ACTIVE;
9938
9939   // if new animation frame was drawn, correct crumbled sand border
9940   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9941     TEST_DrawLevelFieldCrumbled(x, y);
9942 }
9943
9944 static int getSpecialActionElement(int element, int number, int base_element)
9945 {
9946   return (element != EL_EMPTY ? element :
9947           number != -1 ? base_element + number - 1 :
9948           EL_EMPTY);
9949 }
9950
9951 static int getModifiedActionNumber(int value_old, int operator, int operand,
9952                                    int value_min, int value_max)
9953 {
9954   int value_new = (operator == CA_MODE_SET      ? operand :
9955                    operator == CA_MODE_ADD      ? value_old + operand :
9956                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9957                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9958                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9959                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9960                    value_old);
9961
9962   return (value_new < value_min ? value_min :
9963           value_new > value_max ? value_max :
9964           value_new);
9965 }
9966
9967 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9968 {
9969   struct ElementInfo *ei = &element_info[element];
9970   struct ElementChangeInfo *change = &ei->change_page[page];
9971   int target_element = change->target_element;
9972   int action_type = change->action_type;
9973   int action_mode = change->action_mode;
9974   int action_arg = change->action_arg;
9975   int action_element = change->action_element;
9976   int i;
9977
9978   if (!change->has_action)
9979     return;
9980
9981   // ---------- determine action paramater values -----------------------------
9982
9983   int level_time_value =
9984     (level.time > 0 ? TimeLeft :
9985      TimePlayed);
9986
9987   int action_arg_element_raw =
9988     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9989      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9990      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9991      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9992      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9993      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9994      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9995      EL_EMPTY);
9996   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9997
9998   int action_arg_direction =
9999     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10000      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10001      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10002      change->actual_trigger_side :
10003      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10004      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10005      MV_NONE);
10006
10007   int action_arg_number_min =
10008     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10009      CA_ARG_MIN);
10010
10011   int action_arg_number_max =
10012     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10013      action_type == CA_SET_LEVEL_GEMS ? 999 :
10014      action_type == CA_SET_LEVEL_TIME ? 9999 :
10015      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10016      action_type == CA_SET_CE_VALUE ? 9999 :
10017      action_type == CA_SET_CE_SCORE ? 9999 :
10018      CA_ARG_MAX);
10019
10020   int action_arg_number_reset =
10021     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10022      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10023      action_type == CA_SET_LEVEL_TIME ? level.time :
10024      action_type == CA_SET_LEVEL_SCORE ? 0 :
10025      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10026      action_type == CA_SET_CE_SCORE ? 0 :
10027      0);
10028
10029   int action_arg_number =
10030     (action_arg <= CA_ARG_MAX ? action_arg :
10031      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10032      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10033      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10034      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10035      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10036      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10037      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10038      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10039      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10040      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10041      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10042      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10043      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10044      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10045      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10046      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10047      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10048      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10049      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10050      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10051      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10052      -1);
10053
10054   int action_arg_number_old =
10055     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10056      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10057      action_type == CA_SET_LEVEL_SCORE ? game.score :
10058      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10059      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10060      0);
10061
10062   int action_arg_number_new =
10063     getModifiedActionNumber(action_arg_number_old,
10064                             action_mode, action_arg_number,
10065                             action_arg_number_min, action_arg_number_max);
10066
10067   int trigger_player_bits =
10068     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10069      change->actual_trigger_player_bits : change->trigger_player);
10070
10071   int action_arg_player_bits =
10072     (action_arg >= CA_ARG_PLAYER_1 &&
10073      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10074      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10075      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10076      PLAYER_BITS_ANY);
10077
10078   // ---------- execute action  -----------------------------------------------
10079
10080   switch (action_type)
10081   {
10082     case CA_NO_ACTION:
10083     {
10084       return;
10085     }
10086
10087     // ---------- level actions  ----------------------------------------------
10088
10089     case CA_RESTART_LEVEL:
10090     {
10091       game.restart_level = TRUE;
10092
10093       break;
10094     }
10095
10096     case CA_SHOW_ENVELOPE:
10097     {
10098       int element = getSpecialActionElement(action_arg_element,
10099                                             action_arg_number, EL_ENVELOPE_1);
10100
10101       if (IS_ENVELOPE(element))
10102         local_player->show_envelope = element;
10103
10104       break;
10105     }
10106
10107     case CA_SET_LEVEL_TIME:
10108     {
10109       if (level.time > 0)       // only modify limited time value
10110       {
10111         TimeLeft = action_arg_number_new;
10112
10113         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10114
10115         DisplayGameControlValues();
10116
10117         if (!TimeLeft && setup.time_limit)
10118           for (i = 0; i < MAX_PLAYERS; i++)
10119             KillPlayer(&stored_player[i]);
10120       }
10121
10122       break;
10123     }
10124
10125     case CA_SET_LEVEL_SCORE:
10126     {
10127       game.score = action_arg_number_new;
10128
10129       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10130
10131       DisplayGameControlValues();
10132
10133       break;
10134     }
10135
10136     case CA_SET_LEVEL_GEMS:
10137     {
10138       game.gems_still_needed = action_arg_number_new;
10139
10140       game.snapshot.collected_item = TRUE;
10141
10142       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10143
10144       DisplayGameControlValues();
10145
10146       break;
10147     }
10148
10149     case CA_SET_LEVEL_WIND:
10150     {
10151       game.wind_direction = action_arg_direction;
10152
10153       break;
10154     }
10155
10156     case CA_SET_LEVEL_RANDOM_SEED:
10157     {
10158       // ensure that setting a new random seed while playing is predictable
10159       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10160
10161       break;
10162     }
10163
10164     // ---------- player actions  ---------------------------------------------
10165
10166     case CA_MOVE_PLAYER:
10167     case CA_MOVE_PLAYER_NEW:
10168     {
10169       // automatically move to the next field in specified direction
10170       for (i = 0; i < MAX_PLAYERS; i++)
10171         if (trigger_player_bits & (1 << i))
10172           if (action_type == CA_MOVE_PLAYER ||
10173               stored_player[i].MovPos == 0)
10174             stored_player[i].programmed_action = action_arg_direction;
10175
10176       break;
10177     }
10178
10179     case CA_EXIT_PLAYER:
10180     {
10181       for (i = 0; i < MAX_PLAYERS; i++)
10182         if (action_arg_player_bits & (1 << i))
10183           ExitPlayer(&stored_player[i]);
10184
10185       if (game.players_still_needed == 0)
10186         LevelSolved();
10187
10188       break;
10189     }
10190
10191     case CA_KILL_PLAYER:
10192     {
10193       for (i = 0; i < MAX_PLAYERS; i++)
10194         if (action_arg_player_bits & (1 << i))
10195           KillPlayer(&stored_player[i]);
10196
10197       break;
10198     }
10199
10200     case CA_SET_PLAYER_KEYS:
10201     {
10202       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10203       int element = getSpecialActionElement(action_arg_element,
10204                                             action_arg_number, EL_KEY_1);
10205
10206       if (IS_KEY(element))
10207       {
10208         for (i = 0; i < MAX_PLAYERS; i++)
10209         {
10210           if (trigger_player_bits & (1 << i))
10211           {
10212             stored_player[i].key[KEY_NR(element)] = key_state;
10213
10214             DrawGameDoorValues();
10215           }
10216         }
10217       }
10218
10219       break;
10220     }
10221
10222     case CA_SET_PLAYER_SPEED:
10223     {
10224       for (i = 0; i < MAX_PLAYERS; i++)
10225       {
10226         if (trigger_player_bits & (1 << i))
10227         {
10228           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10229
10230           if (action_arg == CA_ARG_SPEED_FASTER &&
10231               stored_player[i].cannot_move)
10232           {
10233             action_arg_number = STEPSIZE_VERY_SLOW;
10234           }
10235           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10236                    action_arg == CA_ARG_SPEED_FASTER)
10237           {
10238             action_arg_number = 2;
10239             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10240                            CA_MODE_MULTIPLY);
10241           }
10242           else if (action_arg == CA_ARG_NUMBER_RESET)
10243           {
10244             action_arg_number = level.initial_player_stepsize[i];
10245           }
10246
10247           move_stepsize =
10248             getModifiedActionNumber(move_stepsize,
10249                                     action_mode,
10250                                     action_arg_number,
10251                                     action_arg_number_min,
10252                                     action_arg_number_max);
10253
10254           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10255         }
10256       }
10257
10258       break;
10259     }
10260
10261     case CA_SET_PLAYER_SHIELD:
10262     {
10263       for (i = 0; i < MAX_PLAYERS; i++)
10264       {
10265         if (trigger_player_bits & (1 << i))
10266         {
10267           if (action_arg == CA_ARG_SHIELD_OFF)
10268           {
10269             stored_player[i].shield_normal_time_left = 0;
10270             stored_player[i].shield_deadly_time_left = 0;
10271           }
10272           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10273           {
10274             stored_player[i].shield_normal_time_left = 999999;
10275           }
10276           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10277           {
10278             stored_player[i].shield_normal_time_left = 999999;
10279             stored_player[i].shield_deadly_time_left = 999999;
10280           }
10281         }
10282       }
10283
10284       break;
10285     }
10286
10287     case CA_SET_PLAYER_GRAVITY:
10288     {
10289       for (i = 0; i < MAX_PLAYERS; i++)
10290       {
10291         if (trigger_player_bits & (1 << i))
10292         {
10293           stored_player[i].gravity =
10294             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10295              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10296              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10297              stored_player[i].gravity);
10298         }
10299       }
10300
10301       break;
10302     }
10303
10304     case CA_SET_PLAYER_ARTWORK:
10305     {
10306       for (i = 0; i < MAX_PLAYERS; i++)
10307       {
10308         if (trigger_player_bits & (1 << i))
10309         {
10310           int artwork_element = action_arg_element;
10311
10312           if (action_arg == CA_ARG_ELEMENT_RESET)
10313             artwork_element =
10314               (level.use_artwork_element[i] ? level.artwork_element[i] :
10315                stored_player[i].element_nr);
10316
10317           if (stored_player[i].artwork_element != artwork_element)
10318             stored_player[i].Frame = 0;
10319
10320           stored_player[i].artwork_element = artwork_element;
10321
10322           SetPlayerWaiting(&stored_player[i], FALSE);
10323
10324           // set number of special actions for bored and sleeping animation
10325           stored_player[i].num_special_action_bored =
10326             get_num_special_action(artwork_element,
10327                                    ACTION_BORING_1, ACTION_BORING_LAST);
10328           stored_player[i].num_special_action_sleeping =
10329             get_num_special_action(artwork_element,
10330                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10331         }
10332       }
10333
10334       break;
10335     }
10336
10337     case CA_SET_PLAYER_INVENTORY:
10338     {
10339       for (i = 0; i < MAX_PLAYERS; i++)
10340       {
10341         struct PlayerInfo *player = &stored_player[i];
10342         int j, k;
10343
10344         if (trigger_player_bits & (1 << i))
10345         {
10346           int inventory_element = action_arg_element;
10347
10348           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10349               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10350               action_arg == CA_ARG_ELEMENT_ACTION)
10351           {
10352             int element = inventory_element;
10353             int collect_count = element_info[element].collect_count_initial;
10354
10355             if (!IS_CUSTOM_ELEMENT(element))
10356               collect_count = 1;
10357
10358             if (collect_count == 0)
10359               player->inventory_infinite_element = element;
10360             else
10361               for (k = 0; k < collect_count; k++)
10362                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10363                   player->inventory_element[player->inventory_size++] =
10364                     element;
10365           }
10366           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10367                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10368                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10369           {
10370             if (player->inventory_infinite_element != EL_UNDEFINED &&
10371                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10372                                      action_arg_element_raw))
10373               player->inventory_infinite_element = EL_UNDEFINED;
10374
10375             for (k = 0, j = 0; j < player->inventory_size; j++)
10376             {
10377               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10378                                         action_arg_element_raw))
10379                 player->inventory_element[k++] = player->inventory_element[j];
10380             }
10381
10382             player->inventory_size = k;
10383           }
10384           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10385           {
10386             if (player->inventory_size > 0)
10387             {
10388               for (j = 0; j < player->inventory_size - 1; j++)
10389                 player->inventory_element[j] = player->inventory_element[j + 1];
10390
10391               player->inventory_size--;
10392             }
10393           }
10394           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10395           {
10396             if (player->inventory_size > 0)
10397               player->inventory_size--;
10398           }
10399           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10400           {
10401             player->inventory_infinite_element = EL_UNDEFINED;
10402             player->inventory_size = 0;
10403           }
10404           else if (action_arg == CA_ARG_INVENTORY_RESET)
10405           {
10406             player->inventory_infinite_element = EL_UNDEFINED;
10407             player->inventory_size = 0;
10408
10409             if (level.use_initial_inventory[i])
10410             {
10411               for (j = 0; j < level.initial_inventory_size[i]; j++)
10412               {
10413                 int element = level.initial_inventory_content[i][j];
10414                 int collect_count = element_info[element].collect_count_initial;
10415
10416                 if (!IS_CUSTOM_ELEMENT(element))
10417                   collect_count = 1;
10418
10419                 if (collect_count == 0)
10420                   player->inventory_infinite_element = element;
10421                 else
10422                   for (k = 0; k < collect_count; k++)
10423                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10424                       player->inventory_element[player->inventory_size++] =
10425                         element;
10426               }
10427             }
10428           }
10429         }
10430       }
10431
10432       break;
10433     }
10434
10435     // ---------- CE actions  -------------------------------------------------
10436
10437     case CA_SET_CE_VALUE:
10438     {
10439       int last_ce_value = CustomValue[x][y];
10440
10441       CustomValue[x][y] = action_arg_number_new;
10442
10443       if (CustomValue[x][y] != last_ce_value)
10444       {
10445         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10446         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10447
10448         if (CustomValue[x][y] == 0)
10449         {
10450           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10451           ChangeCount[x][y] = 0;        // allow at least one more change
10452
10453           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10454           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10455         }
10456       }
10457
10458       break;
10459     }
10460
10461     case CA_SET_CE_SCORE:
10462     {
10463       int last_ce_score = ei->collect_score;
10464
10465       ei->collect_score = action_arg_number_new;
10466
10467       if (ei->collect_score != last_ce_score)
10468       {
10469         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10470         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10471
10472         if (ei->collect_score == 0)
10473         {
10474           int xx, yy;
10475
10476           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10477           ChangeCount[x][y] = 0;        // allow at least one more change
10478
10479           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10480           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10481
10482           /*
10483             This is a very special case that seems to be a mixture between
10484             CheckElementChange() and CheckTriggeredElementChange(): while
10485             the first one only affects single elements that are triggered
10486             directly, the second one affects multiple elements in the playfield
10487             that are triggered indirectly by another element. This is a third
10488             case: Changing the CE score always affects multiple identical CEs,
10489             so every affected CE must be checked, not only the single CE for
10490             which the CE score was changed in the first place (as every instance
10491             of that CE shares the same CE score, and therefore also can change)!
10492           */
10493           SCAN_PLAYFIELD(xx, yy)
10494           {
10495             if (Tile[xx][yy] == element)
10496               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10497                                  CE_SCORE_GETS_ZERO);
10498           }
10499         }
10500       }
10501
10502       break;
10503     }
10504
10505     case CA_SET_CE_ARTWORK:
10506     {
10507       int artwork_element = action_arg_element;
10508       boolean reset_frame = FALSE;
10509       int xx, yy;
10510
10511       if (action_arg == CA_ARG_ELEMENT_RESET)
10512         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10513                            element);
10514
10515       if (ei->gfx_element != artwork_element)
10516         reset_frame = TRUE;
10517
10518       ei->gfx_element = artwork_element;
10519
10520       SCAN_PLAYFIELD(xx, yy)
10521       {
10522         if (Tile[xx][yy] == element)
10523         {
10524           if (reset_frame)
10525           {
10526             ResetGfxAnimation(xx, yy);
10527             ResetRandomAnimationValue(xx, yy);
10528           }
10529
10530           TEST_DrawLevelField(xx, yy);
10531         }
10532       }
10533
10534       break;
10535     }
10536
10537     // ---------- engine actions  ---------------------------------------------
10538
10539     case CA_SET_ENGINE_SCAN_MODE:
10540     {
10541       InitPlayfieldScanMode(action_arg);
10542
10543       break;
10544     }
10545
10546     default:
10547       break;
10548   }
10549 }
10550
10551 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10552 {
10553   int old_element = Tile[x][y];
10554   int new_element = GetElementFromGroupElement(element);
10555   int previous_move_direction = MovDir[x][y];
10556   int last_ce_value = CustomValue[x][y];
10557   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10558   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10559   boolean add_player_onto_element = (new_element_is_player &&
10560                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10561                                      IS_WALKABLE(old_element));
10562
10563   if (!add_player_onto_element)
10564   {
10565     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10566       RemoveMovingField(x, y);
10567     else
10568       RemoveField(x, y);
10569
10570     Tile[x][y] = new_element;
10571
10572     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10573       MovDir[x][y] = previous_move_direction;
10574
10575     if (element_info[new_element].use_last_ce_value)
10576       CustomValue[x][y] = last_ce_value;
10577
10578     InitField_WithBug1(x, y, FALSE);
10579
10580     new_element = Tile[x][y];   // element may have changed
10581
10582     ResetGfxAnimation(x, y);
10583     ResetRandomAnimationValue(x, y);
10584
10585     TEST_DrawLevelField(x, y);
10586
10587     if (GFX_CRUMBLED(new_element))
10588       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10589   }
10590
10591   // check if element under the player changes from accessible to unaccessible
10592   // (needed for special case of dropping element which then changes)
10593   // (must be checked after creating new element for walkable group elements)
10594   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10595       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10596   {
10597     Bang(x, y);
10598
10599     return;
10600   }
10601
10602   // "ChangeCount" not set yet to allow "entered by player" change one time
10603   if (new_element_is_player)
10604     RelocatePlayer(x, y, new_element);
10605
10606   if (is_change)
10607     ChangeCount[x][y]++;        // count number of changes in the same frame
10608
10609   TestIfBadThingTouchesPlayer(x, y);
10610   TestIfPlayerTouchesCustomElement(x, y);
10611   TestIfElementTouchesCustomElement(x, y);
10612 }
10613
10614 static void CreateField(int x, int y, int element)
10615 {
10616   CreateFieldExt(x, y, element, FALSE);
10617 }
10618
10619 static void CreateElementFromChange(int x, int y, int element)
10620 {
10621   element = GET_VALID_RUNTIME_ELEMENT(element);
10622
10623   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10624   {
10625     int old_element = Tile[x][y];
10626
10627     // prevent changed element from moving in same engine frame
10628     // unless both old and new element can either fall or move
10629     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10630         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10631       Stop[x][y] = TRUE;
10632   }
10633
10634   CreateFieldExt(x, y, element, TRUE);
10635 }
10636
10637 static boolean ChangeElement(int x, int y, int element, int page)
10638 {
10639   struct ElementInfo *ei = &element_info[element];
10640   struct ElementChangeInfo *change = &ei->change_page[page];
10641   int ce_value = CustomValue[x][y];
10642   int ce_score = ei->collect_score;
10643   int target_element;
10644   int old_element = Tile[x][y];
10645
10646   // always use default change event to prevent running into a loop
10647   if (ChangeEvent[x][y] == -1)
10648     ChangeEvent[x][y] = CE_DELAY;
10649
10650   if (ChangeEvent[x][y] == CE_DELAY)
10651   {
10652     // reset actual trigger element, trigger player and action element
10653     change->actual_trigger_element = EL_EMPTY;
10654     change->actual_trigger_player = EL_EMPTY;
10655     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10656     change->actual_trigger_side = CH_SIDE_NONE;
10657     change->actual_trigger_ce_value = 0;
10658     change->actual_trigger_ce_score = 0;
10659   }
10660
10661   // do not change elements more than a specified maximum number of changes
10662   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10663     return FALSE;
10664
10665   ChangeCount[x][y]++;          // count number of changes in the same frame
10666
10667   if (change->explode)
10668   {
10669     Bang(x, y);
10670
10671     return TRUE;
10672   }
10673
10674   if (change->use_target_content)
10675   {
10676     boolean complete_replace = TRUE;
10677     boolean can_replace[3][3];
10678     int xx, yy;
10679
10680     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10681     {
10682       boolean is_empty;
10683       boolean is_walkable;
10684       boolean is_diggable;
10685       boolean is_collectible;
10686       boolean is_removable;
10687       boolean is_destructible;
10688       int ex = x + xx - 1;
10689       int ey = y + yy - 1;
10690       int content_element = change->target_content.e[xx][yy];
10691       int e;
10692
10693       can_replace[xx][yy] = TRUE;
10694
10695       if (ex == x && ey == y)   // do not check changing element itself
10696         continue;
10697
10698       if (content_element == EL_EMPTY_SPACE)
10699       {
10700         can_replace[xx][yy] = FALSE;    // do not replace border with space
10701
10702         continue;
10703       }
10704
10705       if (!IN_LEV_FIELD(ex, ey))
10706       {
10707         can_replace[xx][yy] = FALSE;
10708         complete_replace = FALSE;
10709
10710         continue;
10711       }
10712
10713       e = Tile[ex][ey];
10714
10715       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10716         e = MovingOrBlocked2Element(ex, ey);
10717
10718       is_empty = (IS_FREE(ex, ey) ||
10719                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10720
10721       is_walkable     = (is_empty || IS_WALKABLE(e));
10722       is_diggable     = (is_empty || IS_DIGGABLE(e));
10723       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10724       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10725       is_removable    = (is_diggable || is_collectible);
10726
10727       can_replace[xx][yy] =
10728         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10729           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10730           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10731           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10732           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10733           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10734          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10735
10736       if (!can_replace[xx][yy])
10737         complete_replace = FALSE;
10738     }
10739
10740     if (!change->only_if_complete || complete_replace)
10741     {
10742       boolean something_has_changed = FALSE;
10743
10744       if (change->only_if_complete && change->use_random_replace &&
10745           RND(100) < change->random_percentage)
10746         return FALSE;
10747
10748       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10749       {
10750         int ex = x + xx - 1;
10751         int ey = y + yy - 1;
10752         int content_element;
10753
10754         if (can_replace[xx][yy] && (!change->use_random_replace ||
10755                                     RND(100) < change->random_percentage))
10756         {
10757           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10758             RemoveMovingField(ex, ey);
10759
10760           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10761
10762           content_element = change->target_content.e[xx][yy];
10763           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10764                                               ce_value, ce_score);
10765
10766           CreateElementFromChange(ex, ey, target_element);
10767
10768           something_has_changed = TRUE;
10769
10770           // for symmetry reasons, freeze newly created border elements
10771           if (ex != x || ey != y)
10772             Stop[ex][ey] = TRUE;        // no more moving in this frame
10773         }
10774       }
10775
10776       if (something_has_changed)
10777       {
10778         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10779         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10780       }
10781     }
10782   }
10783   else
10784   {
10785     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10786                                         ce_value, ce_score);
10787
10788     if (element == EL_DIAGONAL_GROWING ||
10789         element == EL_DIAGONAL_SHRINKING)
10790     {
10791       target_element = Store[x][y];
10792
10793       Store[x][y] = EL_EMPTY;
10794     }
10795
10796     CreateElementFromChange(x, y, target_element);
10797
10798     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10799     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10800   }
10801
10802   // this uses direct change before indirect change
10803   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10804
10805   return TRUE;
10806 }
10807
10808 static void HandleElementChange(int x, int y, int page)
10809 {
10810   int element = MovingOrBlocked2Element(x, y);
10811   struct ElementInfo *ei = &element_info[element];
10812   struct ElementChangeInfo *change = &ei->change_page[page];
10813   boolean handle_action_before_change = FALSE;
10814
10815 #ifdef DEBUG
10816   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10817       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10818   {
10819     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10820           x, y, element, element_info[element].token_name);
10821     Debug("game:playing:HandleElementChange", "This should never happen!");
10822   }
10823 #endif
10824
10825   // this can happen with classic bombs on walkable, changing elements
10826   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10827   {
10828     return;
10829   }
10830
10831   if (ChangeDelay[x][y] == 0)           // initialize element change
10832   {
10833     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10834
10835     if (change->can_change)
10836     {
10837       // !!! not clear why graphic animation should be reset at all here !!!
10838       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10839       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10840
10841       /*
10842         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10843
10844         When using an animation frame delay of 1 (this only happens with
10845         "sp_zonk.moving.left/right" in the classic graphics), the default
10846         (non-moving) animation shows wrong animation frames (while the
10847         moving animation, like "sp_zonk.moving.left/right", is correct,
10848         so this graphical bug never shows up with the classic graphics).
10849         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10850         be drawn instead of the correct frames 0,1,2,3. This is caused by
10851         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10852         an element change: First when the change delay ("ChangeDelay[][]")
10853         counter has reached zero after decrementing, then a second time in
10854         the next frame (after "GfxFrame[][]" was already incremented) when
10855         "ChangeDelay[][]" is reset to the initial delay value again.
10856
10857         This causes frame 0 to be drawn twice, while the last frame won't
10858         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10859
10860         As some animations may already be cleverly designed around this bug
10861         (at least the "Snake Bite" snake tail animation does this), it cannot
10862         simply be fixed here without breaking such existing animations.
10863         Unfortunately, it cannot easily be detected if a graphics set was
10864         designed "before" or "after" the bug was fixed. As a workaround,
10865         a new graphics set option "game.graphics_engine_version" was added
10866         to be able to specify the game's major release version for which the
10867         graphics set was designed, which can then be used to decide if the
10868         bugfix should be used (version 4 and above) or not (version 3 or
10869         below, or if no version was specified at all, as with old sets).
10870
10871         (The wrong/fixed animation frames can be tested with the test level set
10872         "test_gfxframe" and level "000", which contains a specially prepared
10873         custom element at level position (x/y) == (11/9) which uses the zonk
10874         animation mentioned above. Using "game.graphics_engine_version: 4"
10875         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10876         This can also be seen from the debug output for this test element.)
10877       */
10878
10879       // when a custom element is about to change (for example by change delay),
10880       // do not reset graphic animation when the custom element is moving
10881       if (game.graphics_engine_version < 4 &&
10882           !IS_MOVING(x, y))
10883       {
10884         ResetGfxAnimation(x, y);
10885         ResetRandomAnimationValue(x, y);
10886       }
10887
10888       if (change->pre_change_function)
10889         change->pre_change_function(x, y);
10890     }
10891   }
10892
10893   ChangeDelay[x][y]--;
10894
10895   if (ChangeDelay[x][y] != 0)           // continue element change
10896   {
10897     if (change->can_change)
10898     {
10899       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10900
10901       if (IS_ANIMATED(graphic))
10902         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10903
10904       if (change->change_function)
10905         change->change_function(x, y);
10906     }
10907   }
10908   else                                  // finish element change
10909   {
10910     if (ChangePage[x][y] != -1)         // remember page from delayed change
10911     {
10912       page = ChangePage[x][y];
10913       ChangePage[x][y] = -1;
10914
10915       change = &ei->change_page[page];
10916     }
10917
10918     if (IS_MOVING(x, y))                // never change a running system ;-)
10919     {
10920       ChangeDelay[x][y] = 1;            // try change after next move step
10921       ChangePage[x][y] = page;          // remember page to use for change
10922
10923       return;
10924     }
10925
10926     // special case: set new level random seed before changing element
10927     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10928       handle_action_before_change = TRUE;
10929
10930     if (change->has_action && handle_action_before_change)
10931       ExecuteCustomElementAction(x, y, element, page);
10932
10933     if (change->can_change)
10934     {
10935       if (ChangeElement(x, y, element, page))
10936       {
10937         if (change->post_change_function)
10938           change->post_change_function(x, y);
10939       }
10940     }
10941
10942     if (change->has_action && !handle_action_before_change)
10943       ExecuteCustomElementAction(x, y, element, page);
10944   }
10945 }
10946
10947 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10948                                               int trigger_element,
10949                                               int trigger_event,
10950                                               int trigger_player,
10951                                               int trigger_side,
10952                                               int trigger_page)
10953 {
10954   boolean change_done_any = FALSE;
10955   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10956   int i;
10957
10958   if (!(trigger_events[trigger_element][trigger_event]))
10959     return FALSE;
10960
10961   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10962
10963   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10964   {
10965     int element = EL_CUSTOM_START + i;
10966     boolean change_done = FALSE;
10967     int p;
10968
10969     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10970         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10971       continue;
10972
10973     for (p = 0; p < element_info[element].num_change_pages; p++)
10974     {
10975       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10976
10977       if (change->can_change_or_has_action &&
10978           change->has_event[trigger_event] &&
10979           change->trigger_side & trigger_side &&
10980           change->trigger_player & trigger_player &&
10981           change->trigger_page & trigger_page_bits &&
10982           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10983       {
10984         change->actual_trigger_element = trigger_element;
10985         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10986         change->actual_trigger_player_bits = trigger_player;
10987         change->actual_trigger_side = trigger_side;
10988         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10989         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10990
10991         if ((change->can_change && !change_done) || change->has_action)
10992         {
10993           int x, y;
10994
10995           SCAN_PLAYFIELD(x, y)
10996           {
10997             if (Tile[x][y] == element)
10998             {
10999               if (change->can_change && !change_done)
11000               {
11001                 // if element already changed in this frame, not only prevent
11002                 // another element change (checked in ChangeElement()), but
11003                 // also prevent additional element actions for this element
11004
11005                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11006                     !level.use_action_after_change_bug)
11007                   continue;
11008
11009                 ChangeDelay[x][y] = 1;
11010                 ChangeEvent[x][y] = trigger_event;
11011
11012                 HandleElementChange(x, y, p);
11013               }
11014               else if (change->has_action)
11015               {
11016                 // if element already changed in this frame, not only prevent
11017                 // another element change (checked in ChangeElement()), but
11018                 // also prevent additional element actions for this element
11019
11020                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11021                     !level.use_action_after_change_bug)
11022                   continue;
11023
11024                 ExecuteCustomElementAction(x, y, element, p);
11025                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11026               }
11027             }
11028           }
11029
11030           if (change->can_change)
11031           {
11032             change_done = TRUE;
11033             change_done_any = TRUE;
11034           }
11035         }
11036       }
11037     }
11038   }
11039
11040   RECURSION_LOOP_DETECTION_END();
11041
11042   return change_done_any;
11043 }
11044
11045 static boolean CheckElementChangeExt(int x, int y,
11046                                      int element,
11047                                      int trigger_element,
11048                                      int trigger_event,
11049                                      int trigger_player,
11050                                      int trigger_side)
11051 {
11052   boolean change_done = FALSE;
11053   int p;
11054
11055   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11056       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11057     return FALSE;
11058
11059   if (Tile[x][y] == EL_BLOCKED)
11060   {
11061     Blocked2Moving(x, y, &x, &y);
11062     element = Tile[x][y];
11063   }
11064
11065   // check if element has already changed or is about to change after moving
11066   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11067        Tile[x][y] != element) ||
11068
11069       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11070        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11071         ChangePage[x][y] != -1)))
11072     return FALSE;
11073
11074   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11075
11076   for (p = 0; p < element_info[element].num_change_pages; p++)
11077   {
11078     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11079
11080     /* check trigger element for all events where the element that is checked
11081        for changing interacts with a directly adjacent element -- this is
11082        different to element changes that affect other elements to change on the
11083        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11084     boolean check_trigger_element =
11085       (trigger_event == CE_TOUCHING_X ||
11086        trigger_event == CE_HITTING_X ||
11087        trigger_event == CE_HIT_BY_X ||
11088        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11089
11090     if (change->can_change_or_has_action &&
11091         change->has_event[trigger_event] &&
11092         change->trigger_side & trigger_side &&
11093         change->trigger_player & trigger_player &&
11094         (!check_trigger_element ||
11095          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11096     {
11097       change->actual_trigger_element = trigger_element;
11098       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11099       change->actual_trigger_player_bits = trigger_player;
11100       change->actual_trigger_side = trigger_side;
11101       change->actual_trigger_ce_value = CustomValue[x][y];
11102       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11103
11104       // special case: trigger element not at (x,y) position for some events
11105       if (check_trigger_element)
11106       {
11107         static struct
11108         {
11109           int dx, dy;
11110         } move_xy[] =
11111           {
11112             {  0,  0 },
11113             { -1,  0 },
11114             { +1,  0 },
11115             {  0,  0 },
11116             {  0, -1 },
11117             {  0,  0 }, { 0, 0 }, { 0, 0 },
11118             {  0, +1 }
11119           };
11120
11121         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11122         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11123
11124         change->actual_trigger_ce_value = CustomValue[xx][yy];
11125         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11126       }
11127
11128       if (change->can_change && !change_done)
11129       {
11130         ChangeDelay[x][y] = 1;
11131         ChangeEvent[x][y] = trigger_event;
11132
11133         HandleElementChange(x, y, p);
11134
11135         change_done = TRUE;
11136       }
11137       else if (change->has_action)
11138       {
11139         ExecuteCustomElementAction(x, y, element, p);
11140         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11141       }
11142     }
11143   }
11144
11145   RECURSION_LOOP_DETECTION_END();
11146
11147   return change_done;
11148 }
11149
11150 static void PlayPlayerSound(struct PlayerInfo *player)
11151 {
11152   int jx = player->jx, jy = player->jy;
11153   int sound_element = player->artwork_element;
11154   int last_action = player->last_action_waiting;
11155   int action = player->action_waiting;
11156
11157   if (player->is_waiting)
11158   {
11159     if (action != last_action)
11160       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11161     else
11162       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11163   }
11164   else
11165   {
11166     if (action != last_action)
11167       StopSound(element_info[sound_element].sound[last_action]);
11168
11169     if (last_action == ACTION_SLEEPING)
11170       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11171   }
11172 }
11173
11174 static void PlayAllPlayersSound(void)
11175 {
11176   int i;
11177
11178   for (i = 0; i < MAX_PLAYERS; i++)
11179     if (stored_player[i].active)
11180       PlayPlayerSound(&stored_player[i]);
11181 }
11182
11183 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11184 {
11185   boolean last_waiting = player->is_waiting;
11186   int move_dir = player->MovDir;
11187
11188   player->dir_waiting = move_dir;
11189   player->last_action_waiting = player->action_waiting;
11190
11191   if (is_waiting)
11192   {
11193     if (!last_waiting)          // not waiting -> waiting
11194     {
11195       player->is_waiting = TRUE;
11196
11197       player->frame_counter_bored =
11198         FrameCounter +
11199         game.player_boring_delay_fixed +
11200         GetSimpleRandom(game.player_boring_delay_random);
11201       player->frame_counter_sleeping =
11202         FrameCounter +
11203         game.player_sleeping_delay_fixed +
11204         GetSimpleRandom(game.player_sleeping_delay_random);
11205
11206       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11207     }
11208
11209     if (game.player_sleeping_delay_fixed +
11210         game.player_sleeping_delay_random > 0 &&
11211         player->anim_delay_counter == 0 &&
11212         player->post_delay_counter == 0 &&
11213         FrameCounter >= player->frame_counter_sleeping)
11214       player->is_sleeping = TRUE;
11215     else if (game.player_boring_delay_fixed +
11216              game.player_boring_delay_random > 0 &&
11217              FrameCounter >= player->frame_counter_bored)
11218       player->is_bored = TRUE;
11219
11220     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11221                               player->is_bored ? ACTION_BORING :
11222                               ACTION_WAITING);
11223
11224     if (player->is_sleeping && player->use_murphy)
11225     {
11226       // special case for sleeping Murphy when leaning against non-free tile
11227
11228       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11229           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11230            !IS_MOVING(player->jx - 1, player->jy)))
11231         move_dir = MV_LEFT;
11232       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11233                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11234                 !IS_MOVING(player->jx + 1, player->jy)))
11235         move_dir = MV_RIGHT;
11236       else
11237         player->is_sleeping = FALSE;
11238
11239       player->dir_waiting = move_dir;
11240     }
11241
11242     if (player->is_sleeping)
11243     {
11244       if (player->num_special_action_sleeping > 0)
11245       {
11246         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11247         {
11248           int last_special_action = player->special_action_sleeping;
11249           int num_special_action = player->num_special_action_sleeping;
11250           int special_action =
11251             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11252              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11253              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11254              last_special_action + 1 : ACTION_SLEEPING);
11255           int special_graphic =
11256             el_act_dir2img(player->artwork_element, special_action, move_dir);
11257
11258           player->anim_delay_counter =
11259             graphic_info[special_graphic].anim_delay_fixed +
11260             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11261           player->post_delay_counter =
11262             graphic_info[special_graphic].post_delay_fixed +
11263             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11264
11265           player->special_action_sleeping = special_action;
11266         }
11267
11268         if (player->anim_delay_counter > 0)
11269         {
11270           player->action_waiting = player->special_action_sleeping;
11271           player->anim_delay_counter--;
11272         }
11273         else if (player->post_delay_counter > 0)
11274         {
11275           player->post_delay_counter--;
11276         }
11277       }
11278     }
11279     else if (player->is_bored)
11280     {
11281       if (player->num_special_action_bored > 0)
11282       {
11283         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11284         {
11285           int special_action =
11286             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11287           int special_graphic =
11288             el_act_dir2img(player->artwork_element, special_action, move_dir);
11289
11290           player->anim_delay_counter =
11291             graphic_info[special_graphic].anim_delay_fixed +
11292             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11293           player->post_delay_counter =
11294             graphic_info[special_graphic].post_delay_fixed +
11295             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11296
11297           player->special_action_bored = special_action;
11298         }
11299
11300         if (player->anim_delay_counter > 0)
11301         {
11302           player->action_waiting = player->special_action_bored;
11303           player->anim_delay_counter--;
11304         }
11305         else if (player->post_delay_counter > 0)
11306         {
11307           player->post_delay_counter--;
11308         }
11309       }
11310     }
11311   }
11312   else if (last_waiting)        // waiting -> not waiting
11313   {
11314     player->is_waiting = FALSE;
11315     player->is_bored = FALSE;
11316     player->is_sleeping = FALSE;
11317
11318     player->frame_counter_bored = -1;
11319     player->frame_counter_sleeping = -1;
11320
11321     player->anim_delay_counter = 0;
11322     player->post_delay_counter = 0;
11323
11324     player->dir_waiting = player->MovDir;
11325     player->action_waiting = ACTION_DEFAULT;
11326
11327     player->special_action_bored = ACTION_DEFAULT;
11328     player->special_action_sleeping = ACTION_DEFAULT;
11329   }
11330 }
11331
11332 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11333 {
11334   if ((!player->is_moving  && player->was_moving) ||
11335       (player->MovPos == 0 && player->was_moving) ||
11336       (player->is_snapping && !player->was_snapping) ||
11337       (player->is_dropping && !player->was_dropping))
11338   {
11339     if (!CheckSaveEngineSnapshotToList())
11340       return;
11341
11342     player->was_moving = FALSE;
11343     player->was_snapping = TRUE;
11344     player->was_dropping = TRUE;
11345   }
11346   else
11347   {
11348     if (player->is_moving)
11349       player->was_moving = TRUE;
11350
11351     if (!player->is_snapping)
11352       player->was_snapping = FALSE;
11353
11354     if (!player->is_dropping)
11355       player->was_dropping = FALSE;
11356   }
11357
11358   static struct MouseActionInfo mouse_action_last = { 0 };
11359   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11360   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11361
11362   if (new_released)
11363     CheckSaveEngineSnapshotToList();
11364
11365   mouse_action_last = mouse_action;
11366 }
11367
11368 static void CheckSingleStepMode(struct PlayerInfo *player)
11369 {
11370   if (tape.single_step && tape.recording && !tape.pausing)
11371   {
11372     // as it is called "single step mode", just return to pause mode when the
11373     // player stopped moving after one tile (or never starts moving at all)
11374     // (reverse logic needed here in case single step mode used in team mode)
11375     if (player->is_moving ||
11376         player->is_pushing ||
11377         player->is_dropping_pressed ||
11378         player->effective_mouse_action.button)
11379       game.enter_single_step_mode = FALSE;
11380   }
11381
11382   CheckSaveEngineSnapshot(player);
11383 }
11384
11385 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11386 {
11387   int left      = player_action & JOY_LEFT;
11388   int right     = player_action & JOY_RIGHT;
11389   int up        = player_action & JOY_UP;
11390   int down      = player_action & JOY_DOWN;
11391   int button1   = player_action & JOY_BUTTON_1;
11392   int button2   = player_action & JOY_BUTTON_2;
11393   int dx        = (left ? -1 : right ? 1 : 0);
11394   int dy        = (up   ? -1 : down  ? 1 : 0);
11395
11396   if (!player->active || tape.pausing)
11397     return 0;
11398
11399   if (player_action)
11400   {
11401     if (button1)
11402       SnapField(player, dx, dy);
11403     else
11404     {
11405       if (button2)
11406         DropElement(player);
11407
11408       MovePlayer(player, dx, dy);
11409     }
11410
11411     CheckSingleStepMode(player);
11412
11413     SetPlayerWaiting(player, FALSE);
11414
11415     return player_action;
11416   }
11417   else
11418   {
11419     // no actions for this player (no input at player's configured device)
11420
11421     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11422     SnapField(player, 0, 0);
11423     CheckGravityMovementWhenNotMoving(player);
11424
11425     if (player->MovPos == 0)
11426       SetPlayerWaiting(player, TRUE);
11427
11428     if (player->MovPos == 0)    // needed for tape.playing
11429       player->is_moving = FALSE;
11430
11431     player->is_dropping = FALSE;
11432     player->is_dropping_pressed = FALSE;
11433     player->drop_pressed_delay = 0;
11434
11435     CheckSingleStepMode(player);
11436
11437     return 0;
11438   }
11439 }
11440
11441 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11442                                          byte *tape_action)
11443 {
11444   if (!tape.use_mouse_actions)
11445     return;
11446
11447   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11448   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11449   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11450 }
11451
11452 static void SetTapeActionFromMouseAction(byte *tape_action,
11453                                          struct MouseActionInfo *mouse_action)
11454 {
11455   if (!tape.use_mouse_actions)
11456     return;
11457
11458   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11459   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11460   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11461 }
11462
11463 static void CheckLevelSolved(void)
11464 {
11465   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11466   {
11467     if (game_em.level_solved &&
11468         !game_em.game_over)                             // game won
11469     {
11470       LevelSolved();
11471
11472       game_em.game_over = TRUE;
11473
11474       game.all_players_gone = TRUE;
11475     }
11476
11477     if (game_em.game_over)                              // game lost
11478       game.all_players_gone = TRUE;
11479   }
11480   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11481   {
11482     if (game_sp.level_solved &&
11483         !game_sp.game_over)                             // game won
11484     {
11485       LevelSolved();
11486
11487       game_sp.game_over = TRUE;
11488
11489       game.all_players_gone = TRUE;
11490     }
11491
11492     if (game_sp.game_over)                              // game lost
11493       game.all_players_gone = TRUE;
11494   }
11495   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11496   {
11497     if (game_mm.level_solved &&
11498         !game_mm.game_over)                             // game won
11499     {
11500       LevelSolved();
11501
11502       game_mm.game_over = TRUE;
11503
11504       game.all_players_gone = TRUE;
11505     }
11506
11507     if (game_mm.game_over)                              // game lost
11508       game.all_players_gone = TRUE;
11509   }
11510 }
11511
11512 static void CheckLevelTime(void)
11513 {
11514   int i;
11515
11516   if (TimeFrames >= FRAMES_PER_SECOND)
11517   {
11518     TimeFrames = 0;
11519     TapeTime++;
11520
11521     for (i = 0; i < MAX_PLAYERS; i++)
11522     {
11523       struct PlayerInfo *player = &stored_player[i];
11524
11525       if (SHIELD_ON(player))
11526       {
11527         player->shield_normal_time_left--;
11528
11529         if (player->shield_deadly_time_left > 0)
11530           player->shield_deadly_time_left--;
11531       }
11532     }
11533
11534     if (!game.LevelSolved && !level.use_step_counter)
11535     {
11536       TimePlayed++;
11537
11538       if (TimeLeft > 0)
11539       {
11540         TimeLeft--;
11541
11542         if (TimeLeft <= 10 && setup.time_limit)
11543           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11544
11545         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11546            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11547
11548         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11549
11550         if (!TimeLeft && setup.time_limit)
11551         {
11552           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11553             game_em.lev->killed_out_of_time = TRUE;
11554           else
11555             for (i = 0; i < MAX_PLAYERS; i++)
11556               KillPlayer(&stored_player[i]);
11557         }
11558       }
11559       else if (game.no_time_limit && !game.all_players_gone)
11560       {
11561         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11562       }
11563
11564       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11565     }
11566
11567     if (tape.recording || tape.playing)
11568       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11569   }
11570
11571   if (tape.recording || tape.playing)
11572     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11573
11574   UpdateAndDisplayGameControlValues();
11575 }
11576
11577 void AdvanceFrameAndPlayerCounters(int player_nr)
11578 {
11579   int i;
11580
11581   // advance frame counters (global frame counter and time frame counter)
11582   FrameCounter++;
11583   TimeFrames++;
11584
11585   // advance player counters (counters for move delay, move animation etc.)
11586   for (i = 0; i < MAX_PLAYERS; i++)
11587   {
11588     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11589     int move_delay_value = stored_player[i].move_delay_value;
11590     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11591
11592     if (!advance_player_counters)       // not all players may be affected
11593       continue;
11594
11595     if (move_frames == 0)       // less than one move per game frame
11596     {
11597       int stepsize = TILEX / move_delay_value;
11598       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11599       int count = (stored_player[i].is_moving ?
11600                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11601
11602       if (count % delay == 0)
11603         move_frames = 1;
11604     }
11605
11606     stored_player[i].Frame += move_frames;
11607
11608     if (stored_player[i].MovPos != 0)
11609       stored_player[i].StepFrame += move_frames;
11610
11611     if (stored_player[i].move_delay > 0)
11612       stored_player[i].move_delay--;
11613
11614     // due to bugs in previous versions, counter must count up, not down
11615     if (stored_player[i].push_delay != -1)
11616       stored_player[i].push_delay++;
11617
11618     if (stored_player[i].drop_delay > 0)
11619       stored_player[i].drop_delay--;
11620
11621     if (stored_player[i].is_dropping_pressed)
11622       stored_player[i].drop_pressed_delay++;
11623   }
11624 }
11625
11626 void StartGameActions(boolean init_network_game, boolean record_tape,
11627                       int random_seed)
11628 {
11629   unsigned int new_random_seed = InitRND(random_seed);
11630
11631   if (record_tape)
11632     TapeStartRecording(new_random_seed);
11633
11634   if (init_network_game)
11635   {
11636     SendToServer_LevelFile();
11637     SendToServer_StartPlaying();
11638
11639     return;
11640   }
11641
11642   InitGame();
11643 }
11644
11645 static void GameActionsExt(void)
11646 {
11647 #if 0
11648   static unsigned int game_frame_delay = 0;
11649 #endif
11650   unsigned int game_frame_delay_value;
11651   byte *recorded_player_action;
11652   byte summarized_player_action = 0;
11653   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11654   int i;
11655
11656   // detect endless loops, caused by custom element programming
11657   if (recursion_loop_detected && recursion_loop_depth == 0)
11658   {
11659     char *message = getStringCat3("Internal Error! Element ",
11660                                   EL_NAME(recursion_loop_element),
11661                                   " caused endless loop! Quit the game?");
11662
11663     Warn("element '%s' caused endless loop in game engine",
11664          EL_NAME(recursion_loop_element));
11665
11666     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11667
11668     recursion_loop_detected = FALSE;    // if game should be continued
11669
11670     free(message);
11671
11672     return;
11673   }
11674
11675   if (game.restart_level)
11676     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11677
11678   CheckLevelSolved();
11679
11680   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11681     GameWon();
11682
11683   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11684     TapeStop();
11685
11686   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11687     return;
11688
11689   game_frame_delay_value =
11690     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11691
11692   if (tape.playing && tape.warp_forward && !tape.pausing)
11693     game_frame_delay_value = 0;
11694
11695   SetVideoFrameDelay(game_frame_delay_value);
11696
11697   // (de)activate virtual buttons depending on current game status
11698   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11699   {
11700     if (game.all_players_gone)  // if no players there to be controlled anymore
11701       SetOverlayActive(FALSE);
11702     else if (!tape.playing)     // if game continues after tape stopped playing
11703       SetOverlayActive(TRUE);
11704   }
11705
11706 #if 0
11707 #if 0
11708   // ---------- main game synchronization point ----------
11709
11710   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11711
11712   Debug("game:playing:skip", "skip == %d", skip);
11713
11714 #else
11715   // ---------- main game synchronization point ----------
11716
11717   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11718 #endif
11719 #endif
11720
11721   if (network_playing && !network_player_action_received)
11722   {
11723     // try to get network player actions in time
11724
11725     // last chance to get network player actions without main loop delay
11726     HandleNetworking();
11727
11728     // game was quit by network peer
11729     if (game_status != GAME_MODE_PLAYING)
11730       return;
11731
11732     // check if network player actions still missing and game still running
11733     if (!network_player_action_received && !checkGameEnded())
11734       return;           // failed to get network player actions in time
11735
11736     // do not yet reset "network_player_action_received" (for tape.pausing)
11737   }
11738
11739   if (tape.pausing)
11740     return;
11741
11742   // at this point we know that we really continue executing the game
11743
11744   network_player_action_received = FALSE;
11745
11746   // when playing tape, read previously recorded player input from tape data
11747   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11748
11749   local_player->effective_mouse_action = local_player->mouse_action;
11750
11751   if (recorded_player_action != NULL)
11752     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11753                                  recorded_player_action);
11754
11755   // TapePlayAction() may return NULL when toggling to "pause before death"
11756   if (tape.pausing)
11757     return;
11758
11759   if (tape.set_centered_player)
11760   {
11761     game.centered_player_nr_next = tape.centered_player_nr_next;
11762     game.set_centered_player = TRUE;
11763   }
11764
11765   for (i = 0; i < MAX_PLAYERS; i++)
11766   {
11767     summarized_player_action |= stored_player[i].action;
11768
11769     if (!network_playing && (game.team_mode || tape.playing))
11770       stored_player[i].effective_action = stored_player[i].action;
11771   }
11772
11773   if (network_playing && !checkGameEnded())
11774     SendToServer_MovePlayer(summarized_player_action);
11775
11776   // summarize all actions at local players mapped input device position
11777   // (this allows using different input devices in single player mode)
11778   if (!network.enabled && !game.team_mode)
11779     stored_player[map_player_action[local_player->index_nr]].effective_action =
11780       summarized_player_action;
11781
11782   // summarize all actions at centered player in local team mode
11783   if (tape.recording &&
11784       setup.team_mode && !network.enabled &&
11785       setup.input_on_focus &&
11786       game.centered_player_nr != -1)
11787   {
11788     for (i = 0; i < MAX_PLAYERS; i++)
11789       stored_player[map_player_action[i]].effective_action =
11790         (i == game.centered_player_nr ? summarized_player_action : 0);
11791   }
11792
11793   if (recorded_player_action != NULL)
11794     for (i = 0; i < MAX_PLAYERS; i++)
11795       stored_player[i].effective_action = recorded_player_action[i];
11796
11797   for (i = 0; i < MAX_PLAYERS; i++)
11798   {
11799     tape_action[i] = stored_player[i].effective_action;
11800
11801     /* (this may happen in the RND game engine if a player was not present on
11802        the playfield on level start, but appeared later from a custom element */
11803     if (setup.team_mode &&
11804         tape.recording &&
11805         tape_action[i] &&
11806         !tape.player_participates[i])
11807       tape.player_participates[i] = TRUE;
11808   }
11809
11810   SetTapeActionFromMouseAction(tape_action,
11811                                &local_player->effective_mouse_action);
11812
11813   // only record actions from input devices, but not programmed actions
11814   if (tape.recording)
11815     TapeRecordAction(tape_action);
11816
11817   // remember if game was played (especially after tape stopped playing)
11818   if (!tape.playing && summarized_player_action)
11819     game.GamePlayed = TRUE;
11820
11821 #if USE_NEW_PLAYER_ASSIGNMENTS
11822   // !!! also map player actions in single player mode !!!
11823   // if (game.team_mode)
11824   if (1)
11825   {
11826     byte mapped_action[MAX_PLAYERS];
11827
11828 #if DEBUG_PLAYER_ACTIONS
11829     for (i = 0; i < MAX_PLAYERS; i++)
11830       DebugContinued("", "%d, ", stored_player[i].effective_action);
11831 #endif
11832
11833     for (i = 0; i < MAX_PLAYERS; i++)
11834       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11835
11836     for (i = 0; i < MAX_PLAYERS; i++)
11837       stored_player[i].effective_action = mapped_action[i];
11838
11839 #if DEBUG_PLAYER_ACTIONS
11840     DebugContinued("", "=> ");
11841     for (i = 0; i < MAX_PLAYERS; i++)
11842       DebugContinued("", "%d, ", stored_player[i].effective_action);
11843     DebugContinued("game:playing:player", "\n");
11844 #endif
11845   }
11846 #if DEBUG_PLAYER_ACTIONS
11847   else
11848   {
11849     for (i = 0; i < MAX_PLAYERS; i++)
11850       DebugContinued("", "%d, ", stored_player[i].effective_action);
11851     DebugContinued("game:playing:player", "\n");
11852   }
11853 #endif
11854 #endif
11855
11856   for (i = 0; i < MAX_PLAYERS; i++)
11857   {
11858     // allow engine snapshot in case of changed movement attempt
11859     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11860         (stored_player[i].effective_action & KEY_MOTION))
11861       game.snapshot.changed_action = TRUE;
11862
11863     // allow engine snapshot in case of snapping/dropping attempt
11864     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11865         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11866       game.snapshot.changed_action = TRUE;
11867
11868     game.snapshot.last_action[i] = stored_player[i].effective_action;
11869   }
11870
11871   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11872   {
11873     GameActions_EM_Main();
11874   }
11875   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11876   {
11877     GameActions_SP_Main();
11878   }
11879   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11880   {
11881     GameActions_MM_Main();
11882   }
11883   else
11884   {
11885     GameActions_RND_Main();
11886   }
11887
11888   BlitScreenToBitmap(backbuffer);
11889
11890   CheckLevelSolved();
11891   CheckLevelTime();
11892
11893   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11894
11895   if (global.show_frames_per_second)
11896   {
11897     static unsigned int fps_counter = 0;
11898     static int fps_frames = 0;
11899     unsigned int fps_delay_ms = Counter() - fps_counter;
11900
11901     fps_frames++;
11902
11903     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11904     {
11905       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11906
11907       fps_frames = 0;
11908       fps_counter = Counter();
11909
11910       // always draw FPS to screen after FPS value was updated
11911       redraw_mask |= REDRAW_FPS;
11912     }
11913
11914     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11915     if (GetDrawDeactivationMask() == REDRAW_NONE)
11916       redraw_mask |= REDRAW_FPS;
11917   }
11918 }
11919
11920 static void GameActions_CheckSaveEngineSnapshot(void)
11921 {
11922   if (!game.snapshot.save_snapshot)
11923     return;
11924
11925   // clear flag for saving snapshot _before_ saving snapshot
11926   game.snapshot.save_snapshot = FALSE;
11927
11928   SaveEngineSnapshotToList();
11929 }
11930
11931 void GameActions(void)
11932 {
11933   GameActionsExt();
11934
11935   GameActions_CheckSaveEngineSnapshot();
11936 }
11937
11938 void GameActions_EM_Main(void)
11939 {
11940   byte effective_action[MAX_PLAYERS];
11941   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11942   int i;
11943
11944   for (i = 0; i < MAX_PLAYERS; i++)
11945     effective_action[i] = stored_player[i].effective_action;
11946
11947   GameActions_EM(effective_action, warp_mode);
11948 }
11949
11950 void GameActions_SP_Main(void)
11951 {
11952   byte effective_action[MAX_PLAYERS];
11953   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11954   int i;
11955
11956   for (i = 0; i < MAX_PLAYERS; i++)
11957     effective_action[i] = stored_player[i].effective_action;
11958
11959   GameActions_SP(effective_action, warp_mode);
11960
11961   for (i = 0; i < MAX_PLAYERS; i++)
11962   {
11963     if (stored_player[i].force_dropping)
11964       stored_player[i].action |= KEY_BUTTON_DROP;
11965
11966     stored_player[i].force_dropping = FALSE;
11967   }
11968 }
11969
11970 void GameActions_MM_Main(void)
11971 {
11972   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11973
11974   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11975 }
11976
11977 void GameActions_RND_Main(void)
11978 {
11979   GameActions_RND();
11980 }
11981
11982 void GameActions_RND(void)
11983 {
11984   static struct MouseActionInfo mouse_action_last = { 0 };
11985   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11986   int magic_wall_x = 0, magic_wall_y = 0;
11987   int i, x, y, element, graphic, last_gfx_frame;
11988
11989   InitPlayfieldScanModeVars();
11990
11991   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11992   {
11993     SCAN_PLAYFIELD(x, y)
11994     {
11995       ChangeCount[x][y] = 0;
11996       ChangeEvent[x][y] = -1;
11997     }
11998   }
11999
12000   if (game.set_centered_player)
12001   {
12002     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12003
12004     // switching to "all players" only possible if all players fit to screen
12005     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12006     {
12007       game.centered_player_nr_next = game.centered_player_nr;
12008       game.set_centered_player = FALSE;
12009     }
12010
12011     // do not switch focus to non-existing (or non-active) player
12012     if (game.centered_player_nr_next >= 0 &&
12013         !stored_player[game.centered_player_nr_next].active)
12014     {
12015       game.centered_player_nr_next = game.centered_player_nr;
12016       game.set_centered_player = FALSE;
12017     }
12018   }
12019
12020   if (game.set_centered_player &&
12021       ScreenMovPos == 0)        // screen currently aligned at tile position
12022   {
12023     int sx, sy;
12024
12025     if (game.centered_player_nr_next == -1)
12026     {
12027       setScreenCenteredToAllPlayers(&sx, &sy);
12028     }
12029     else
12030     {
12031       sx = stored_player[game.centered_player_nr_next].jx;
12032       sy = stored_player[game.centered_player_nr_next].jy;
12033     }
12034
12035     game.centered_player_nr = game.centered_player_nr_next;
12036     game.set_centered_player = FALSE;
12037
12038     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12039     DrawGameDoorValues();
12040   }
12041
12042   // check single step mode (set flag and clear again if any player is active)
12043   game.enter_single_step_mode =
12044     (tape.single_step && tape.recording && !tape.pausing);
12045
12046   for (i = 0; i < MAX_PLAYERS; i++)
12047   {
12048     int actual_player_action = stored_player[i].effective_action;
12049
12050 #if 1
12051     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12052        - rnd_equinox_tetrachloride 048
12053        - rnd_equinox_tetrachloride_ii 096
12054        - rnd_emanuel_schmieg 002
12055        - doctor_sloan_ww 001, 020
12056     */
12057     if (stored_player[i].MovPos == 0)
12058       CheckGravityMovement(&stored_player[i]);
12059 #endif
12060
12061     // overwrite programmed action with tape action
12062     if (stored_player[i].programmed_action)
12063       actual_player_action = stored_player[i].programmed_action;
12064
12065     PlayerActions(&stored_player[i], actual_player_action);
12066
12067     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12068   }
12069
12070   // single step pause mode may already have been toggled by "ScrollPlayer()"
12071   if (game.enter_single_step_mode && !tape.pausing)
12072     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12073
12074   ScrollScreen(NULL, SCROLL_GO_ON);
12075
12076   /* for backwards compatibility, the following code emulates a fixed bug that
12077      occured when pushing elements (causing elements that just made their last
12078      pushing step to already (if possible) make their first falling step in the
12079      same game frame, which is bad); this code is also needed to use the famous
12080      "spring push bug" which is used in older levels and might be wanted to be
12081      used also in newer levels, but in this case the buggy pushing code is only
12082      affecting the "spring" element and no other elements */
12083
12084   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12085   {
12086     for (i = 0; i < MAX_PLAYERS; i++)
12087     {
12088       struct PlayerInfo *player = &stored_player[i];
12089       int x = player->jx;
12090       int y = player->jy;
12091
12092       if (player->active && player->is_pushing && player->is_moving &&
12093           IS_MOVING(x, y) &&
12094           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12095            Tile[x][y] == EL_SPRING))
12096       {
12097         ContinueMoving(x, y);
12098
12099         // continue moving after pushing (this is actually a bug)
12100         if (!IS_MOVING(x, y))
12101           Stop[x][y] = FALSE;
12102       }
12103     }
12104   }
12105
12106   SCAN_PLAYFIELD(x, y)
12107   {
12108     Last[x][y] = Tile[x][y];
12109
12110     ChangeCount[x][y] = 0;
12111     ChangeEvent[x][y] = -1;
12112
12113     // this must be handled before main playfield loop
12114     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12115     {
12116       MovDelay[x][y]--;
12117       if (MovDelay[x][y] <= 0)
12118         RemoveField(x, y);
12119     }
12120
12121     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12122     {
12123       MovDelay[x][y]--;
12124       if (MovDelay[x][y] <= 0)
12125       {
12126         int element = Store[x][y];
12127         int move_direction = MovDir[x][y];
12128         int player_index_bit = Store2[x][y];
12129
12130         Store[x][y] = 0;
12131         Store2[x][y] = 0;
12132
12133         RemoveField(x, y);
12134         TEST_DrawLevelField(x, y);
12135
12136         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12137       }
12138     }
12139
12140 #if DEBUG
12141     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12142     {
12143       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12144             x, y);
12145       Debug("game:playing:GameActions_RND", "This should never happen!");
12146
12147       ChangePage[x][y] = -1;
12148     }
12149 #endif
12150
12151     Stop[x][y] = FALSE;
12152     if (WasJustMoving[x][y] > 0)
12153       WasJustMoving[x][y]--;
12154     if (WasJustFalling[x][y] > 0)
12155       WasJustFalling[x][y]--;
12156     if (CheckCollision[x][y] > 0)
12157       CheckCollision[x][y]--;
12158     if (CheckImpact[x][y] > 0)
12159       CheckImpact[x][y]--;
12160
12161     GfxFrame[x][y]++;
12162
12163     /* reset finished pushing action (not done in ContinueMoving() to allow
12164        continuous pushing animation for elements with zero push delay) */
12165     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12166     {
12167       ResetGfxAnimation(x, y);
12168       TEST_DrawLevelField(x, y);
12169     }
12170
12171 #if DEBUG
12172     if (IS_BLOCKED(x, y))
12173     {
12174       int oldx, oldy;
12175
12176       Blocked2Moving(x, y, &oldx, &oldy);
12177       if (!IS_MOVING(oldx, oldy))
12178       {
12179         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12180         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12181         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12182         Debug("game:playing:GameActions_RND", "This should never happen!");
12183       }
12184     }
12185 #endif
12186   }
12187
12188   if (mouse_action.button)
12189   {
12190     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12191     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12192
12193     x = mouse_action.lx;
12194     y = mouse_action.ly;
12195     element = Tile[x][y];
12196
12197     if (new_button)
12198     {
12199       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12200       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12201                                          ch_button);
12202     }
12203
12204     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12205     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12206                                        ch_button);
12207   }
12208
12209   SCAN_PLAYFIELD(x, y)
12210   {
12211     element = Tile[x][y];
12212     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12213     last_gfx_frame = GfxFrame[x][y];
12214
12215     ResetGfxFrame(x, y);
12216
12217     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12218       DrawLevelGraphicAnimation(x, y, graphic);
12219
12220     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12221         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12222       ResetRandomAnimationValue(x, y);
12223
12224     SetRandomAnimationValue(x, y);
12225
12226     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12227
12228     if (IS_INACTIVE(element))
12229     {
12230       if (IS_ANIMATED(graphic))
12231         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12232
12233       continue;
12234     }
12235
12236     // this may take place after moving, so 'element' may have changed
12237     if (IS_CHANGING(x, y) &&
12238         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12239     {
12240       int page = element_info[element].event_page_nr[CE_DELAY];
12241
12242       HandleElementChange(x, y, page);
12243
12244       element = Tile[x][y];
12245       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12246     }
12247
12248     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12249     {
12250       StartMoving(x, y);
12251
12252       element = Tile[x][y];
12253       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12254
12255       if (IS_ANIMATED(graphic) &&
12256           !IS_MOVING(x, y) &&
12257           !Stop[x][y])
12258         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12259
12260       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12261         TEST_DrawTwinkleOnField(x, y);
12262     }
12263     else if (element == EL_ACID)
12264     {
12265       if (!Stop[x][y])
12266         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12267     }
12268     else if ((element == EL_EXIT_OPEN ||
12269               element == EL_EM_EXIT_OPEN ||
12270               element == EL_SP_EXIT_OPEN ||
12271               element == EL_STEEL_EXIT_OPEN ||
12272               element == EL_EM_STEEL_EXIT_OPEN ||
12273               element == EL_SP_TERMINAL ||
12274               element == EL_SP_TERMINAL_ACTIVE ||
12275               element == EL_EXTRA_TIME ||
12276               element == EL_SHIELD_NORMAL ||
12277               element == EL_SHIELD_DEADLY) &&
12278              IS_ANIMATED(graphic))
12279       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12280     else if (IS_MOVING(x, y))
12281       ContinueMoving(x, y);
12282     else if (IS_ACTIVE_BOMB(element))
12283       CheckDynamite(x, y);
12284     else if (element == EL_AMOEBA_GROWING)
12285       AmoebaGrowing(x, y);
12286     else if (element == EL_AMOEBA_SHRINKING)
12287       AmoebaShrinking(x, y);
12288
12289 #if !USE_NEW_AMOEBA_CODE
12290     else if (IS_AMOEBALIVE(element))
12291       AmoebaReproduce(x, y);
12292 #endif
12293
12294     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12295       Life(x, y);
12296     else if (element == EL_EXIT_CLOSED)
12297       CheckExit(x, y);
12298     else if (element == EL_EM_EXIT_CLOSED)
12299       CheckExitEM(x, y);
12300     else if (element == EL_STEEL_EXIT_CLOSED)
12301       CheckExitSteel(x, y);
12302     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12303       CheckExitSteelEM(x, y);
12304     else if (element == EL_SP_EXIT_CLOSED)
12305       CheckExitSP(x, y);
12306     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12307              element == EL_EXPANDABLE_STEELWALL_GROWING)
12308       MauerWaechst(x, y);
12309     else if (element == EL_EXPANDABLE_WALL ||
12310              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12311              element == EL_EXPANDABLE_WALL_VERTICAL ||
12312              element == EL_EXPANDABLE_WALL_ANY ||
12313              element == EL_BD_EXPANDABLE_WALL)
12314       MauerAbleger(x, y);
12315     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12316              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12317              element == EL_EXPANDABLE_STEELWALL_ANY)
12318       MauerAblegerStahl(x, y);
12319     else if (element == EL_FLAMES)
12320       CheckForDragon(x, y);
12321     else if (element == EL_EXPLOSION)
12322       ; // drawing of correct explosion animation is handled separately
12323     else if (element == EL_ELEMENT_SNAPPING ||
12324              element == EL_DIAGONAL_SHRINKING ||
12325              element == EL_DIAGONAL_GROWING)
12326     {
12327       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12328
12329       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12330     }
12331     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12332       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12333
12334     if (IS_BELT_ACTIVE(element))
12335       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12336
12337     if (game.magic_wall_active)
12338     {
12339       int jx = local_player->jx, jy = local_player->jy;
12340
12341       // play the element sound at the position nearest to the player
12342       if ((element == EL_MAGIC_WALL_FULL ||
12343            element == EL_MAGIC_WALL_ACTIVE ||
12344            element == EL_MAGIC_WALL_EMPTYING ||
12345            element == EL_BD_MAGIC_WALL_FULL ||
12346            element == EL_BD_MAGIC_WALL_ACTIVE ||
12347            element == EL_BD_MAGIC_WALL_EMPTYING ||
12348            element == EL_DC_MAGIC_WALL_FULL ||
12349            element == EL_DC_MAGIC_WALL_ACTIVE ||
12350            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12351           ABS(x - jx) + ABS(y - jy) <
12352           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12353       {
12354         magic_wall_x = x;
12355         magic_wall_y = y;
12356       }
12357     }
12358   }
12359
12360 #if USE_NEW_AMOEBA_CODE
12361   // new experimental amoeba growth stuff
12362   if (!(FrameCounter % 8))
12363   {
12364     static unsigned int random = 1684108901;
12365
12366     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12367     {
12368       x = RND(lev_fieldx);
12369       y = RND(lev_fieldy);
12370       element = Tile[x][y];
12371
12372       if (!IS_PLAYER(x,y) &&
12373           (element == EL_EMPTY ||
12374            CAN_GROW_INTO(element) ||
12375            element == EL_QUICKSAND_EMPTY ||
12376            element == EL_QUICKSAND_FAST_EMPTY ||
12377            element == EL_ACID_SPLASH_LEFT ||
12378            element == EL_ACID_SPLASH_RIGHT))
12379       {
12380         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12381             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12382             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12383             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12384           Tile[x][y] = EL_AMOEBA_DROP;
12385       }
12386
12387       random = random * 129 + 1;
12388     }
12389   }
12390 #endif
12391
12392   game.explosions_delayed = FALSE;
12393
12394   SCAN_PLAYFIELD(x, y)
12395   {
12396     element = Tile[x][y];
12397
12398     if (ExplodeField[x][y])
12399       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12400     else if (element == EL_EXPLOSION)
12401       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12402
12403     ExplodeField[x][y] = EX_TYPE_NONE;
12404   }
12405
12406   game.explosions_delayed = TRUE;
12407
12408   if (game.magic_wall_active)
12409   {
12410     if (!(game.magic_wall_time_left % 4))
12411     {
12412       int element = Tile[magic_wall_x][magic_wall_y];
12413
12414       if (element == EL_BD_MAGIC_WALL_FULL ||
12415           element == EL_BD_MAGIC_WALL_ACTIVE ||
12416           element == EL_BD_MAGIC_WALL_EMPTYING)
12417         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12418       else if (element == EL_DC_MAGIC_WALL_FULL ||
12419                element == EL_DC_MAGIC_WALL_ACTIVE ||
12420                element == EL_DC_MAGIC_WALL_EMPTYING)
12421         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12422       else
12423         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12424     }
12425
12426     if (game.magic_wall_time_left > 0)
12427     {
12428       game.magic_wall_time_left--;
12429
12430       if (!game.magic_wall_time_left)
12431       {
12432         SCAN_PLAYFIELD(x, y)
12433         {
12434           element = Tile[x][y];
12435
12436           if (element == EL_MAGIC_WALL_ACTIVE ||
12437               element == EL_MAGIC_WALL_FULL)
12438           {
12439             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12440             TEST_DrawLevelField(x, y);
12441           }
12442           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12443                    element == EL_BD_MAGIC_WALL_FULL)
12444           {
12445             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12446             TEST_DrawLevelField(x, y);
12447           }
12448           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12449                    element == EL_DC_MAGIC_WALL_FULL)
12450           {
12451             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12452             TEST_DrawLevelField(x, y);
12453           }
12454         }
12455
12456         game.magic_wall_active = FALSE;
12457       }
12458     }
12459   }
12460
12461   if (game.light_time_left > 0)
12462   {
12463     game.light_time_left--;
12464
12465     if (game.light_time_left == 0)
12466       RedrawAllLightSwitchesAndInvisibleElements();
12467   }
12468
12469   if (game.timegate_time_left > 0)
12470   {
12471     game.timegate_time_left--;
12472
12473     if (game.timegate_time_left == 0)
12474       CloseAllOpenTimegates();
12475   }
12476
12477   if (game.lenses_time_left > 0)
12478   {
12479     game.lenses_time_left--;
12480
12481     if (game.lenses_time_left == 0)
12482       RedrawAllInvisibleElementsForLenses();
12483   }
12484
12485   if (game.magnify_time_left > 0)
12486   {
12487     game.magnify_time_left--;
12488
12489     if (game.magnify_time_left == 0)
12490       RedrawAllInvisibleElementsForMagnifier();
12491   }
12492
12493   for (i = 0; i < MAX_PLAYERS; i++)
12494   {
12495     struct PlayerInfo *player = &stored_player[i];
12496
12497     if (SHIELD_ON(player))
12498     {
12499       if (player->shield_deadly_time_left)
12500         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12501       else if (player->shield_normal_time_left)
12502         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12503     }
12504   }
12505
12506 #if USE_DELAYED_GFX_REDRAW
12507   SCAN_PLAYFIELD(x, y)
12508   {
12509     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12510     {
12511       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12512          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12513
12514       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12515         DrawLevelField(x, y);
12516
12517       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12518         DrawLevelFieldCrumbled(x, y);
12519
12520       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12521         DrawLevelFieldCrumbledNeighbours(x, y);
12522
12523       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12524         DrawTwinkleOnField(x, y);
12525     }
12526
12527     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12528   }
12529 #endif
12530
12531   DrawAllPlayers();
12532   PlayAllPlayersSound();
12533
12534   for (i = 0; i < MAX_PLAYERS; i++)
12535   {
12536     struct PlayerInfo *player = &stored_player[i];
12537
12538     if (player->show_envelope != 0 && (!player->active ||
12539                                        player->MovPos == 0))
12540     {
12541       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12542
12543       player->show_envelope = 0;
12544     }
12545   }
12546
12547   // use random number generator in every frame to make it less predictable
12548   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12549     RND(1);
12550
12551   mouse_action_last = mouse_action;
12552 }
12553
12554 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12555 {
12556   int min_x = x, min_y = y, max_x = x, max_y = y;
12557   int scr_fieldx = getScreenFieldSizeX();
12558   int scr_fieldy = getScreenFieldSizeY();
12559   int i;
12560
12561   for (i = 0; i < MAX_PLAYERS; i++)
12562   {
12563     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12564
12565     if (!stored_player[i].active || &stored_player[i] == player)
12566       continue;
12567
12568     min_x = MIN(min_x, jx);
12569     min_y = MIN(min_y, jy);
12570     max_x = MAX(max_x, jx);
12571     max_y = MAX(max_y, jy);
12572   }
12573
12574   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12575 }
12576
12577 static boolean AllPlayersInVisibleScreen(void)
12578 {
12579   int i;
12580
12581   for (i = 0; i < MAX_PLAYERS; i++)
12582   {
12583     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12584
12585     if (!stored_player[i].active)
12586       continue;
12587
12588     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12589       return FALSE;
12590   }
12591
12592   return TRUE;
12593 }
12594
12595 void ScrollLevel(int dx, int dy)
12596 {
12597   int scroll_offset = 2 * TILEX_VAR;
12598   int x, y;
12599
12600   BlitBitmap(drawto_field, drawto_field,
12601              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12602              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12603              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12604              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12605              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12606              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12607
12608   if (dx != 0)
12609   {
12610     x = (dx == 1 ? BX1 : BX2);
12611     for (y = BY1; y <= BY2; y++)
12612       DrawScreenField(x, y);
12613   }
12614
12615   if (dy != 0)
12616   {
12617     y = (dy == 1 ? BY1 : BY2);
12618     for (x = BX1; x <= BX2; x++)
12619       DrawScreenField(x, y);
12620   }
12621
12622   redraw_mask |= REDRAW_FIELD;
12623 }
12624
12625 static boolean canFallDown(struct PlayerInfo *player)
12626 {
12627   int jx = player->jx, jy = player->jy;
12628
12629   return (IN_LEV_FIELD(jx, jy + 1) &&
12630           (IS_FREE(jx, jy + 1) ||
12631            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12632           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12633           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12634 }
12635
12636 static boolean canPassField(int x, int y, int move_dir)
12637 {
12638   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12639   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12640   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12641   int nextx = x + dx;
12642   int nexty = y + dy;
12643   int element = Tile[x][y];
12644
12645   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12646           !CAN_MOVE(element) &&
12647           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12648           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12649           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12650 }
12651
12652 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12653 {
12654   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12655   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12656   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12657   int newx = x + dx;
12658   int newy = y + dy;
12659
12660   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12661           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12662           (IS_DIGGABLE(Tile[newx][newy]) ||
12663            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12664            canPassField(newx, newy, move_dir)));
12665 }
12666
12667 static void CheckGravityMovement(struct PlayerInfo *player)
12668 {
12669   if (player->gravity && !player->programmed_action)
12670   {
12671     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12672     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12673     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12674     int jx = player->jx, jy = player->jy;
12675     boolean player_is_moving_to_valid_field =
12676       (!player_is_snapping &&
12677        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12678         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12679     boolean player_can_fall_down = canFallDown(player);
12680
12681     if (player_can_fall_down &&
12682         !player_is_moving_to_valid_field)
12683       player->programmed_action = MV_DOWN;
12684   }
12685 }
12686
12687 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12688 {
12689   return CheckGravityMovement(player);
12690
12691   if (player->gravity && !player->programmed_action)
12692   {
12693     int jx = player->jx, jy = player->jy;
12694     boolean field_under_player_is_free =
12695       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12696     boolean player_is_standing_on_valid_field =
12697       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12698        (IS_WALKABLE(Tile[jx][jy]) &&
12699         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12700
12701     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12702       player->programmed_action = MV_DOWN;
12703   }
12704 }
12705
12706 /*
12707   MovePlayerOneStep()
12708   -----------------------------------------------------------------------------
12709   dx, dy:               direction (non-diagonal) to try to move the player to
12710   real_dx, real_dy:     direction as read from input device (can be diagonal)
12711 */
12712
12713 boolean MovePlayerOneStep(struct PlayerInfo *player,
12714                           int dx, int dy, int real_dx, int real_dy)
12715 {
12716   int jx = player->jx, jy = player->jy;
12717   int new_jx = jx + dx, new_jy = jy + dy;
12718   int can_move;
12719   boolean player_can_move = !player->cannot_move;
12720
12721   if (!player->active || (!dx && !dy))
12722     return MP_NO_ACTION;
12723
12724   player->MovDir = (dx < 0 ? MV_LEFT :
12725                     dx > 0 ? MV_RIGHT :
12726                     dy < 0 ? MV_UP :
12727                     dy > 0 ? MV_DOWN :  MV_NONE);
12728
12729   if (!IN_LEV_FIELD(new_jx, new_jy))
12730     return MP_NO_ACTION;
12731
12732   if (!player_can_move)
12733   {
12734     if (player->MovPos == 0)
12735     {
12736       player->is_moving = FALSE;
12737       player->is_digging = FALSE;
12738       player->is_collecting = FALSE;
12739       player->is_snapping = FALSE;
12740       player->is_pushing = FALSE;
12741     }
12742   }
12743
12744   if (!network.enabled && game.centered_player_nr == -1 &&
12745       !AllPlayersInSight(player, new_jx, new_jy))
12746     return MP_NO_ACTION;
12747
12748   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12749   if (can_move != MP_MOVING)
12750     return can_move;
12751
12752   // check if DigField() has caused relocation of the player
12753   if (player->jx != jx || player->jy != jy)
12754     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12755
12756   StorePlayer[jx][jy] = 0;
12757   player->last_jx = jx;
12758   player->last_jy = jy;
12759   player->jx = new_jx;
12760   player->jy = new_jy;
12761   StorePlayer[new_jx][new_jy] = player->element_nr;
12762
12763   if (player->move_delay_value_next != -1)
12764   {
12765     player->move_delay_value = player->move_delay_value_next;
12766     player->move_delay_value_next = -1;
12767   }
12768
12769   player->MovPos =
12770     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12771
12772   player->step_counter++;
12773
12774   PlayerVisit[jx][jy] = FrameCounter;
12775
12776   player->is_moving = TRUE;
12777
12778 #if 1
12779   // should better be called in MovePlayer(), but this breaks some tapes
12780   ScrollPlayer(player, SCROLL_INIT);
12781 #endif
12782
12783   return MP_MOVING;
12784 }
12785
12786 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12787 {
12788   int jx = player->jx, jy = player->jy;
12789   int old_jx = jx, old_jy = jy;
12790   int moved = MP_NO_ACTION;
12791
12792   if (!player->active)
12793     return FALSE;
12794
12795   if (!dx && !dy)
12796   {
12797     if (player->MovPos == 0)
12798     {
12799       player->is_moving = FALSE;
12800       player->is_digging = FALSE;
12801       player->is_collecting = FALSE;
12802       player->is_snapping = FALSE;
12803       player->is_pushing = FALSE;
12804     }
12805
12806     return FALSE;
12807   }
12808
12809   if (player->move_delay > 0)
12810     return FALSE;
12811
12812   player->move_delay = -1;              // set to "uninitialized" value
12813
12814   // store if player is automatically moved to next field
12815   player->is_auto_moving = (player->programmed_action != MV_NONE);
12816
12817   // remove the last programmed player action
12818   player->programmed_action = 0;
12819
12820   if (player->MovPos)
12821   {
12822     // should only happen if pre-1.2 tape recordings are played
12823     // this is only for backward compatibility
12824
12825     int original_move_delay_value = player->move_delay_value;
12826
12827 #if DEBUG
12828     Debug("game:playing:MovePlayer",
12829           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12830           tape.counter);
12831 #endif
12832
12833     // scroll remaining steps with finest movement resolution
12834     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12835
12836     while (player->MovPos)
12837     {
12838       ScrollPlayer(player, SCROLL_GO_ON);
12839       ScrollScreen(NULL, SCROLL_GO_ON);
12840
12841       AdvanceFrameAndPlayerCounters(player->index_nr);
12842
12843       DrawAllPlayers();
12844       BackToFront_WithFrameDelay(0);
12845     }
12846
12847     player->move_delay_value = original_move_delay_value;
12848   }
12849
12850   player->is_active = FALSE;
12851
12852   if (player->last_move_dir & MV_HORIZONTAL)
12853   {
12854     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12855       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12856   }
12857   else
12858   {
12859     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12860       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12861   }
12862
12863   if (!moved && !player->is_active)
12864   {
12865     player->is_moving = FALSE;
12866     player->is_digging = FALSE;
12867     player->is_collecting = FALSE;
12868     player->is_snapping = FALSE;
12869     player->is_pushing = FALSE;
12870   }
12871
12872   jx = player->jx;
12873   jy = player->jy;
12874
12875   if (moved & MP_MOVING && !ScreenMovPos &&
12876       (player->index_nr == game.centered_player_nr ||
12877        game.centered_player_nr == -1))
12878   {
12879     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12880
12881     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12882     {
12883       // actual player has left the screen -- scroll in that direction
12884       if (jx != old_jx)         // player has moved horizontally
12885         scroll_x += (jx - old_jx);
12886       else                      // player has moved vertically
12887         scroll_y += (jy - old_jy);
12888     }
12889     else
12890     {
12891       int offset_raw = game.scroll_delay_value;
12892
12893       if (jx != old_jx)         // player has moved horizontally
12894       {
12895         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12896         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12897         int new_scroll_x = jx - MIDPOSX + offset_x;
12898
12899         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12900             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12901           scroll_x = new_scroll_x;
12902
12903         // don't scroll over playfield boundaries
12904         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12905
12906         // don't scroll more than one field at a time
12907         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12908
12909         // don't scroll against the player's moving direction
12910         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12911             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12912           scroll_x = old_scroll_x;
12913       }
12914       else                      // player has moved vertically
12915       {
12916         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12917         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12918         int new_scroll_y = jy - MIDPOSY + offset_y;
12919
12920         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12921             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12922           scroll_y = new_scroll_y;
12923
12924         // don't scroll over playfield boundaries
12925         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12926
12927         // don't scroll more than one field at a time
12928         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12929
12930         // don't scroll against the player's moving direction
12931         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12932             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12933           scroll_y = old_scroll_y;
12934       }
12935     }
12936
12937     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12938     {
12939       if (!network.enabled && game.centered_player_nr == -1 &&
12940           !AllPlayersInVisibleScreen())
12941       {
12942         scroll_x = old_scroll_x;
12943         scroll_y = old_scroll_y;
12944       }
12945       else
12946       {
12947         ScrollScreen(player, SCROLL_INIT);
12948         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12949       }
12950     }
12951   }
12952
12953   player->StepFrame = 0;
12954
12955   if (moved & MP_MOVING)
12956   {
12957     if (old_jx != jx && old_jy == jy)
12958       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12959     else if (old_jx == jx && old_jy != jy)
12960       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12961
12962     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12963
12964     player->last_move_dir = player->MovDir;
12965     player->is_moving = TRUE;
12966     player->is_snapping = FALSE;
12967     player->is_switching = FALSE;
12968     player->is_dropping = FALSE;
12969     player->is_dropping_pressed = FALSE;
12970     player->drop_pressed_delay = 0;
12971
12972 #if 0
12973     // should better be called here than above, but this breaks some tapes
12974     ScrollPlayer(player, SCROLL_INIT);
12975 #endif
12976   }
12977   else
12978   {
12979     CheckGravityMovementWhenNotMoving(player);
12980
12981     player->is_moving = FALSE;
12982
12983     /* at this point, the player is allowed to move, but cannot move right now
12984        (e.g. because of something blocking the way) -- ensure that the player
12985        is also allowed to move in the next frame (in old versions before 3.1.1,
12986        the player was forced to wait again for eight frames before next try) */
12987
12988     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12989       player->move_delay = 0;   // allow direct movement in the next frame
12990   }
12991
12992   if (player->move_delay == -1)         // not yet initialized by DigField()
12993     player->move_delay = player->move_delay_value;
12994
12995   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12996   {
12997     TestIfPlayerTouchesBadThing(jx, jy);
12998     TestIfPlayerTouchesCustomElement(jx, jy);
12999   }
13000
13001   if (!player->active)
13002     RemovePlayer(player);
13003
13004   return moved;
13005 }
13006
13007 void ScrollPlayer(struct PlayerInfo *player, int mode)
13008 {
13009   int jx = player->jx, jy = player->jy;
13010   int last_jx = player->last_jx, last_jy = player->last_jy;
13011   int move_stepsize = TILEX / player->move_delay_value;
13012
13013   if (!player->active)
13014     return;
13015
13016   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13017     return;
13018
13019   if (mode == SCROLL_INIT)
13020   {
13021     player->actual_frame_counter = FrameCounter;
13022     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13023
13024     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13025         Tile[last_jx][last_jy] == EL_EMPTY)
13026     {
13027       int last_field_block_delay = 0;   // start with no blocking at all
13028       int block_delay_adjustment = player->block_delay_adjustment;
13029
13030       // if player blocks last field, add delay for exactly one move
13031       if (player->block_last_field)
13032       {
13033         last_field_block_delay += player->move_delay_value;
13034
13035         // when blocking enabled, prevent moving up despite gravity
13036         if (player->gravity && player->MovDir == MV_UP)
13037           block_delay_adjustment = -1;
13038       }
13039
13040       // add block delay adjustment (also possible when not blocking)
13041       last_field_block_delay += block_delay_adjustment;
13042
13043       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13044       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13045     }
13046
13047     if (player->MovPos != 0)    // player has not yet reached destination
13048       return;
13049   }
13050   else if (!FrameReached(&player->actual_frame_counter, 1))
13051     return;
13052
13053   if (player->MovPos != 0)
13054   {
13055     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13056     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13057
13058     // before DrawPlayer() to draw correct player graphic for this case
13059     if (player->MovPos == 0)
13060       CheckGravityMovement(player);
13061   }
13062
13063   if (player->MovPos == 0)      // player reached destination field
13064   {
13065     if (player->move_delay_reset_counter > 0)
13066     {
13067       player->move_delay_reset_counter--;
13068
13069       if (player->move_delay_reset_counter == 0)
13070       {
13071         // continue with normal speed after quickly moving through gate
13072         HALVE_PLAYER_SPEED(player);
13073
13074         // be able to make the next move without delay
13075         player->move_delay = 0;
13076       }
13077     }
13078
13079     player->last_jx = jx;
13080     player->last_jy = jy;
13081
13082     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13083         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13084         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13085         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13086         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13087         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13088         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13089         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13090     {
13091       ExitPlayer(player);
13092
13093       if (game.players_still_needed == 0 &&
13094           (game.friends_still_needed == 0 ||
13095            IS_SP_ELEMENT(Tile[jx][jy])))
13096         LevelSolved();
13097     }
13098
13099     // this breaks one level: "machine", level 000
13100     {
13101       int move_direction = player->MovDir;
13102       int enter_side = MV_DIR_OPPOSITE(move_direction);
13103       int leave_side = move_direction;
13104       int old_jx = last_jx;
13105       int old_jy = last_jy;
13106       int old_element = Tile[old_jx][old_jy];
13107       int new_element = Tile[jx][jy];
13108
13109       if (IS_CUSTOM_ELEMENT(old_element))
13110         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13111                                    CE_LEFT_BY_PLAYER,
13112                                    player->index_bit, leave_side);
13113
13114       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13115                                           CE_PLAYER_LEAVES_X,
13116                                           player->index_bit, leave_side);
13117
13118       if (IS_CUSTOM_ELEMENT(new_element))
13119         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13120                                    player->index_bit, enter_side);
13121
13122       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13123                                           CE_PLAYER_ENTERS_X,
13124                                           player->index_bit, enter_side);
13125
13126       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13127                                         CE_MOVE_OF_X, move_direction);
13128     }
13129
13130     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13131     {
13132       TestIfPlayerTouchesBadThing(jx, jy);
13133       TestIfPlayerTouchesCustomElement(jx, jy);
13134
13135       /* needed because pushed element has not yet reached its destination,
13136          so it would trigger a change event at its previous field location */
13137       if (!player->is_pushing)
13138         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13139
13140       if (level.finish_dig_collect &&
13141           (player->is_digging || player->is_collecting))
13142       {
13143         int last_element = player->last_removed_element;
13144         int move_direction = player->MovDir;
13145         int enter_side = MV_DIR_OPPOSITE(move_direction);
13146         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13147                             CE_PLAYER_COLLECTS_X);
13148
13149         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13150                                             player->index_bit, enter_side);
13151
13152         player->last_removed_element = EL_UNDEFINED;
13153       }
13154
13155       if (!player->active)
13156         RemovePlayer(player);
13157     }
13158
13159     if (level.use_step_counter)
13160     {
13161       int i;
13162
13163       TimePlayed++;
13164
13165       if (TimeLeft > 0)
13166       {
13167         TimeLeft--;
13168
13169         if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
13170           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13171
13172         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13173
13174         DisplayGameControlValues();
13175
13176         if (!TimeLeft && setup.time_limit && !game.LevelSolved)
13177           for (i = 0; i < MAX_PLAYERS; i++)
13178             KillPlayer(&stored_player[i]);
13179       }
13180       else if (game.no_time_limit && !game.all_players_gone)
13181       {
13182         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13183
13184         DisplayGameControlValues();
13185       }
13186     }
13187
13188     if (tape.single_step && tape.recording && !tape.pausing &&
13189         !player->programmed_action)
13190       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13191
13192     if (!player->programmed_action)
13193       CheckSaveEngineSnapshot(player);
13194   }
13195 }
13196
13197 void ScrollScreen(struct PlayerInfo *player, int mode)
13198 {
13199   static unsigned int screen_frame_counter = 0;
13200
13201   if (mode == SCROLL_INIT)
13202   {
13203     // set scrolling step size according to actual player's moving speed
13204     ScrollStepSize = TILEX / player->move_delay_value;
13205
13206     screen_frame_counter = FrameCounter;
13207     ScreenMovDir = player->MovDir;
13208     ScreenMovPos = player->MovPos;
13209     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13210     return;
13211   }
13212   else if (!FrameReached(&screen_frame_counter, 1))
13213     return;
13214
13215   if (ScreenMovPos)
13216   {
13217     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13218     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13219     redraw_mask |= REDRAW_FIELD;
13220   }
13221   else
13222     ScreenMovDir = MV_NONE;
13223 }
13224
13225 void TestIfPlayerTouchesCustomElement(int x, int y)
13226 {
13227   static int xy[4][2] =
13228   {
13229     { 0, -1 },
13230     { -1, 0 },
13231     { +1, 0 },
13232     { 0, +1 }
13233   };
13234   static int trigger_sides[4][2] =
13235   {
13236     // center side       border side
13237     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13238     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13239     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13240     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13241   };
13242   static int touch_dir[4] =
13243   {
13244     MV_LEFT | MV_RIGHT,
13245     MV_UP   | MV_DOWN,
13246     MV_UP   | MV_DOWN,
13247     MV_LEFT | MV_RIGHT
13248   };
13249   int center_element = Tile[x][y];      // should always be non-moving!
13250   int i;
13251
13252   for (i = 0; i < NUM_DIRECTIONS; i++)
13253   {
13254     int xx = x + xy[i][0];
13255     int yy = y + xy[i][1];
13256     int center_side = trigger_sides[i][0];
13257     int border_side = trigger_sides[i][1];
13258     int border_element;
13259
13260     if (!IN_LEV_FIELD(xx, yy))
13261       continue;
13262
13263     if (IS_PLAYER(x, y))                // player found at center element
13264     {
13265       struct PlayerInfo *player = PLAYERINFO(x, y);
13266
13267       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13268         border_element = Tile[xx][yy];          // may be moving!
13269       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13270         border_element = Tile[xx][yy];
13271       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13272         border_element = MovingOrBlocked2Element(xx, yy);
13273       else
13274         continue;               // center and border element do not touch
13275
13276       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13277                                  player->index_bit, border_side);
13278       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13279                                           CE_PLAYER_TOUCHES_X,
13280                                           player->index_bit, border_side);
13281
13282       {
13283         /* use player element that is initially defined in the level playfield,
13284            not the player element that corresponds to the runtime player number
13285            (example: a level that contains EL_PLAYER_3 as the only player would
13286            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13287         int player_element = PLAYERINFO(x, y)->initial_element;
13288
13289         CheckElementChangeBySide(xx, yy, border_element, player_element,
13290                                  CE_TOUCHING_X, border_side);
13291       }
13292     }
13293     else if (IS_PLAYER(xx, yy))         // player found at border element
13294     {
13295       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13296
13297       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13298       {
13299         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13300           continue;             // center and border element do not touch
13301       }
13302
13303       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13304                                  player->index_bit, center_side);
13305       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13306                                           CE_PLAYER_TOUCHES_X,
13307                                           player->index_bit, center_side);
13308
13309       {
13310         /* use player element that is initially defined in the level playfield,
13311            not the player element that corresponds to the runtime player number
13312            (example: a level that contains EL_PLAYER_3 as the only player would
13313            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13314         int player_element = PLAYERINFO(xx, yy)->initial_element;
13315
13316         CheckElementChangeBySide(x, y, center_element, player_element,
13317                                  CE_TOUCHING_X, center_side);
13318       }
13319
13320       break;
13321     }
13322   }
13323 }
13324
13325 void TestIfElementTouchesCustomElement(int x, int y)
13326 {
13327   static int xy[4][2] =
13328   {
13329     { 0, -1 },
13330     { -1, 0 },
13331     { +1, 0 },
13332     { 0, +1 }
13333   };
13334   static int trigger_sides[4][2] =
13335   {
13336     // center side      border side
13337     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13338     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13339     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13340     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13341   };
13342   static int touch_dir[4] =
13343   {
13344     MV_LEFT | MV_RIGHT,
13345     MV_UP   | MV_DOWN,
13346     MV_UP   | MV_DOWN,
13347     MV_LEFT | MV_RIGHT
13348   };
13349   boolean change_center_element = FALSE;
13350   int center_element = Tile[x][y];      // should always be non-moving!
13351   int border_element_old[NUM_DIRECTIONS];
13352   int i;
13353
13354   for (i = 0; i < NUM_DIRECTIONS; i++)
13355   {
13356     int xx = x + xy[i][0];
13357     int yy = y + xy[i][1];
13358     int border_element;
13359
13360     border_element_old[i] = -1;
13361
13362     if (!IN_LEV_FIELD(xx, yy))
13363       continue;
13364
13365     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13366       border_element = Tile[xx][yy];    // may be moving!
13367     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13368       border_element = Tile[xx][yy];
13369     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13370       border_element = MovingOrBlocked2Element(xx, yy);
13371     else
13372       continue;                 // center and border element do not touch
13373
13374     border_element_old[i] = border_element;
13375   }
13376
13377   for (i = 0; i < NUM_DIRECTIONS; i++)
13378   {
13379     int xx = x + xy[i][0];
13380     int yy = y + xy[i][1];
13381     int center_side = trigger_sides[i][0];
13382     int border_element = border_element_old[i];
13383
13384     if (border_element == -1)
13385       continue;
13386
13387     // check for change of border element
13388     CheckElementChangeBySide(xx, yy, border_element, center_element,
13389                              CE_TOUCHING_X, center_side);
13390
13391     // (center element cannot be player, so we dont have to check this here)
13392   }
13393
13394   for (i = 0; i < NUM_DIRECTIONS; i++)
13395   {
13396     int xx = x + xy[i][0];
13397     int yy = y + xy[i][1];
13398     int border_side = trigger_sides[i][1];
13399     int border_element = border_element_old[i];
13400
13401     if (border_element == -1)
13402       continue;
13403
13404     // check for change of center element (but change it only once)
13405     if (!change_center_element)
13406       change_center_element =
13407         CheckElementChangeBySide(x, y, center_element, border_element,
13408                                  CE_TOUCHING_X, border_side);
13409
13410     if (IS_PLAYER(xx, yy))
13411     {
13412       /* use player element that is initially defined in the level playfield,
13413          not the player element that corresponds to the runtime player number
13414          (example: a level that contains EL_PLAYER_3 as the only player would
13415          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13416       int player_element = PLAYERINFO(xx, yy)->initial_element;
13417
13418       CheckElementChangeBySide(x, y, center_element, player_element,
13419                                CE_TOUCHING_X, border_side);
13420     }
13421   }
13422 }
13423
13424 void TestIfElementHitsCustomElement(int x, int y, int direction)
13425 {
13426   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13427   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13428   int hitx = x + dx, hity = y + dy;
13429   int hitting_element = Tile[x][y];
13430   int touched_element;
13431
13432   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13433     return;
13434
13435   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13436                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13437
13438   if (IN_LEV_FIELD(hitx, hity))
13439   {
13440     int opposite_direction = MV_DIR_OPPOSITE(direction);
13441     int hitting_side = direction;
13442     int touched_side = opposite_direction;
13443     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13444                           MovDir[hitx][hity] != direction ||
13445                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13446
13447     object_hit = TRUE;
13448
13449     if (object_hit)
13450     {
13451       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13452                                CE_HITTING_X, touched_side);
13453
13454       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13455                                CE_HIT_BY_X, hitting_side);
13456
13457       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13458                                CE_HIT_BY_SOMETHING, opposite_direction);
13459
13460       if (IS_PLAYER(hitx, hity))
13461       {
13462         /* use player element that is initially defined in the level playfield,
13463            not the player element that corresponds to the runtime player number
13464            (example: a level that contains EL_PLAYER_3 as the only player would
13465            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13466         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13467
13468         CheckElementChangeBySide(x, y, hitting_element, player_element,
13469                                  CE_HITTING_X, touched_side);
13470       }
13471     }
13472   }
13473
13474   // "hitting something" is also true when hitting the playfield border
13475   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13476                            CE_HITTING_SOMETHING, direction);
13477 }
13478
13479 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13480 {
13481   int i, kill_x = -1, kill_y = -1;
13482
13483   int bad_element = -1;
13484   static int test_xy[4][2] =
13485   {
13486     { 0, -1 },
13487     { -1, 0 },
13488     { +1, 0 },
13489     { 0, +1 }
13490   };
13491   static int test_dir[4] =
13492   {
13493     MV_UP,
13494     MV_LEFT,
13495     MV_RIGHT,
13496     MV_DOWN
13497   };
13498
13499   for (i = 0; i < NUM_DIRECTIONS; i++)
13500   {
13501     int test_x, test_y, test_move_dir, test_element;
13502
13503     test_x = good_x + test_xy[i][0];
13504     test_y = good_y + test_xy[i][1];
13505
13506     if (!IN_LEV_FIELD(test_x, test_y))
13507       continue;
13508
13509     test_move_dir =
13510       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13511
13512     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13513
13514     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13515        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13516     */
13517     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13518         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13519     {
13520       kill_x = test_x;
13521       kill_y = test_y;
13522       bad_element = test_element;
13523
13524       break;
13525     }
13526   }
13527
13528   if (kill_x != -1 || kill_y != -1)
13529   {
13530     if (IS_PLAYER(good_x, good_y))
13531     {
13532       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13533
13534       if (player->shield_deadly_time_left > 0 &&
13535           !IS_INDESTRUCTIBLE(bad_element))
13536         Bang(kill_x, kill_y);
13537       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13538         KillPlayer(player);
13539     }
13540     else
13541       Bang(good_x, good_y);
13542   }
13543 }
13544
13545 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13546 {
13547   int i, kill_x = -1, kill_y = -1;
13548   int bad_element = Tile[bad_x][bad_y];
13549   static int test_xy[4][2] =
13550   {
13551     { 0, -1 },
13552     { -1, 0 },
13553     { +1, 0 },
13554     { 0, +1 }
13555   };
13556   static int touch_dir[4] =
13557   {
13558     MV_LEFT | MV_RIGHT,
13559     MV_UP   | MV_DOWN,
13560     MV_UP   | MV_DOWN,
13561     MV_LEFT | MV_RIGHT
13562   };
13563   static int test_dir[4] =
13564   {
13565     MV_UP,
13566     MV_LEFT,
13567     MV_RIGHT,
13568     MV_DOWN
13569   };
13570
13571   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13572     return;
13573
13574   for (i = 0; i < NUM_DIRECTIONS; i++)
13575   {
13576     int test_x, test_y, test_move_dir, test_element;
13577
13578     test_x = bad_x + test_xy[i][0];
13579     test_y = bad_y + test_xy[i][1];
13580
13581     if (!IN_LEV_FIELD(test_x, test_y))
13582       continue;
13583
13584     test_move_dir =
13585       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13586
13587     test_element = Tile[test_x][test_y];
13588
13589     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13590        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13591     */
13592     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13593         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13594     {
13595       // good thing is player or penguin that does not move away
13596       if (IS_PLAYER(test_x, test_y))
13597       {
13598         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13599
13600         if (bad_element == EL_ROBOT && player->is_moving)
13601           continue;     // robot does not kill player if he is moving
13602
13603         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13604         {
13605           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13606             continue;           // center and border element do not touch
13607         }
13608
13609         kill_x = test_x;
13610         kill_y = test_y;
13611
13612         break;
13613       }
13614       else if (test_element == EL_PENGUIN)
13615       {
13616         kill_x = test_x;
13617         kill_y = test_y;
13618
13619         break;
13620       }
13621     }
13622   }
13623
13624   if (kill_x != -1 || kill_y != -1)
13625   {
13626     if (IS_PLAYER(kill_x, kill_y))
13627     {
13628       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13629
13630       if (player->shield_deadly_time_left > 0 &&
13631           !IS_INDESTRUCTIBLE(bad_element))
13632         Bang(bad_x, bad_y);
13633       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13634         KillPlayer(player);
13635     }
13636     else
13637       Bang(kill_x, kill_y);
13638   }
13639 }
13640
13641 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13642 {
13643   int bad_element = Tile[bad_x][bad_y];
13644   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13645   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13646   int test_x = bad_x + dx, test_y = bad_y + dy;
13647   int test_move_dir, test_element;
13648   int kill_x = -1, kill_y = -1;
13649
13650   if (!IN_LEV_FIELD(test_x, test_y))
13651     return;
13652
13653   test_move_dir =
13654     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13655
13656   test_element = Tile[test_x][test_y];
13657
13658   if (test_move_dir != bad_move_dir)
13659   {
13660     // good thing can be player or penguin that does not move away
13661     if (IS_PLAYER(test_x, test_y))
13662     {
13663       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13664
13665       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13666          player as being hit when he is moving towards the bad thing, because
13667          the "get hit by" condition would be lost after the player stops) */
13668       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13669         return;         // player moves away from bad thing
13670
13671       kill_x = test_x;
13672       kill_y = test_y;
13673     }
13674     else if (test_element == EL_PENGUIN)
13675     {
13676       kill_x = test_x;
13677       kill_y = test_y;
13678     }
13679   }
13680
13681   if (kill_x != -1 || kill_y != -1)
13682   {
13683     if (IS_PLAYER(kill_x, kill_y))
13684     {
13685       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13686
13687       if (player->shield_deadly_time_left > 0 &&
13688           !IS_INDESTRUCTIBLE(bad_element))
13689         Bang(bad_x, bad_y);
13690       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13691         KillPlayer(player);
13692     }
13693     else
13694       Bang(kill_x, kill_y);
13695   }
13696 }
13697
13698 void TestIfPlayerTouchesBadThing(int x, int y)
13699 {
13700   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13701 }
13702
13703 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13704 {
13705   TestIfGoodThingHitsBadThing(x, y, move_dir);
13706 }
13707
13708 void TestIfBadThingTouchesPlayer(int x, int y)
13709 {
13710   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13711 }
13712
13713 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13714 {
13715   TestIfBadThingHitsGoodThing(x, y, move_dir);
13716 }
13717
13718 void TestIfFriendTouchesBadThing(int x, int y)
13719 {
13720   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13721 }
13722
13723 void TestIfBadThingTouchesFriend(int x, int y)
13724 {
13725   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13726 }
13727
13728 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13729 {
13730   int i, kill_x = bad_x, kill_y = bad_y;
13731   static int xy[4][2] =
13732   {
13733     { 0, -1 },
13734     { -1, 0 },
13735     { +1, 0 },
13736     { 0, +1 }
13737   };
13738
13739   for (i = 0; i < NUM_DIRECTIONS; i++)
13740   {
13741     int x, y, element;
13742
13743     x = bad_x + xy[i][0];
13744     y = bad_y + xy[i][1];
13745     if (!IN_LEV_FIELD(x, y))
13746       continue;
13747
13748     element = Tile[x][y];
13749     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13750         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13751     {
13752       kill_x = x;
13753       kill_y = y;
13754       break;
13755     }
13756   }
13757
13758   if (kill_x != bad_x || kill_y != bad_y)
13759     Bang(bad_x, bad_y);
13760 }
13761
13762 void KillPlayer(struct PlayerInfo *player)
13763 {
13764   int jx = player->jx, jy = player->jy;
13765
13766   if (!player->active)
13767     return;
13768
13769 #if 0
13770   Debug("game:playing:KillPlayer",
13771         "0: killed == %d, active == %d, reanimated == %d",
13772         player->killed, player->active, player->reanimated);
13773 #endif
13774
13775   /* the following code was introduced to prevent an infinite loop when calling
13776      -> Bang()
13777      -> CheckTriggeredElementChangeExt()
13778      -> ExecuteCustomElementAction()
13779      -> KillPlayer()
13780      -> (infinitely repeating the above sequence of function calls)
13781      which occurs when killing the player while having a CE with the setting
13782      "kill player X when explosion of <player X>"; the solution using a new
13783      field "player->killed" was chosen for backwards compatibility, although
13784      clever use of the fields "player->active" etc. would probably also work */
13785 #if 1
13786   if (player->killed)
13787     return;
13788 #endif
13789
13790   player->killed = TRUE;
13791
13792   // remove accessible field at the player's position
13793   Tile[jx][jy] = EL_EMPTY;
13794
13795   // deactivate shield (else Bang()/Explode() would not work right)
13796   player->shield_normal_time_left = 0;
13797   player->shield_deadly_time_left = 0;
13798
13799 #if 0
13800   Debug("game:playing:KillPlayer",
13801         "1: killed == %d, active == %d, reanimated == %d",
13802         player->killed, player->active, player->reanimated);
13803 #endif
13804
13805   Bang(jx, jy);
13806
13807 #if 0
13808   Debug("game:playing:KillPlayer",
13809         "2: killed == %d, active == %d, reanimated == %d",
13810         player->killed, player->active, player->reanimated);
13811 #endif
13812
13813   if (player->reanimated)       // killed player may have been reanimated
13814     player->killed = player->reanimated = FALSE;
13815   else
13816     BuryPlayer(player);
13817 }
13818
13819 static void KillPlayerUnlessEnemyProtected(int x, int y)
13820 {
13821   if (!PLAYER_ENEMY_PROTECTED(x, y))
13822     KillPlayer(PLAYERINFO(x, y));
13823 }
13824
13825 static void KillPlayerUnlessExplosionProtected(int x, int y)
13826 {
13827   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13828     KillPlayer(PLAYERINFO(x, y));
13829 }
13830
13831 void BuryPlayer(struct PlayerInfo *player)
13832 {
13833   int jx = player->jx, jy = player->jy;
13834
13835   if (!player->active)
13836     return;
13837
13838   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13839   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13840
13841   RemovePlayer(player);
13842
13843   player->buried = TRUE;
13844
13845   if (game.all_players_gone)
13846     game.GameOver = TRUE;
13847 }
13848
13849 void RemovePlayer(struct PlayerInfo *player)
13850 {
13851   int jx = player->jx, jy = player->jy;
13852   int i, found = FALSE;
13853
13854   player->present = FALSE;
13855   player->active = FALSE;
13856
13857   // required for some CE actions (even if the player is not active anymore)
13858   player->MovPos = 0;
13859
13860   if (!ExplodeField[jx][jy])
13861     StorePlayer[jx][jy] = 0;
13862
13863   if (player->is_moving)
13864     TEST_DrawLevelField(player->last_jx, player->last_jy);
13865
13866   for (i = 0; i < MAX_PLAYERS; i++)
13867     if (stored_player[i].active)
13868       found = TRUE;
13869
13870   if (!found)
13871   {
13872     game.all_players_gone = TRUE;
13873     game.GameOver = TRUE;
13874   }
13875
13876   game.exit_x = game.robot_wheel_x = jx;
13877   game.exit_y = game.robot_wheel_y = jy;
13878 }
13879
13880 void ExitPlayer(struct PlayerInfo *player)
13881 {
13882   DrawPlayer(player);   // needed here only to cleanup last field
13883   RemovePlayer(player);
13884
13885   if (game.players_still_needed > 0)
13886     game.players_still_needed--;
13887 }
13888
13889 static void SetFieldForSnapping(int x, int y, int element, int direction,
13890                                 int player_index_bit)
13891 {
13892   struct ElementInfo *ei = &element_info[element];
13893   int direction_bit = MV_DIR_TO_BIT(direction);
13894   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13895   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13896                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13897
13898   Tile[x][y] = EL_ELEMENT_SNAPPING;
13899   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13900   MovDir[x][y] = direction;
13901   Store[x][y] = element;
13902   Store2[x][y] = player_index_bit;
13903
13904   ResetGfxAnimation(x, y);
13905
13906   GfxElement[x][y] = element;
13907   GfxAction[x][y] = action;
13908   GfxDir[x][y] = direction;
13909   GfxFrame[x][y] = -1;
13910 }
13911
13912 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13913                                    int player_index_bit)
13914 {
13915   TestIfElementTouchesCustomElement(x, y);      // for empty space
13916
13917   if (level.finish_dig_collect)
13918   {
13919     int dig_side = MV_DIR_OPPOSITE(direction);
13920
13921     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13922                                         player_index_bit, dig_side);
13923   }
13924 }
13925
13926 /*
13927   =============================================================================
13928   checkDiagonalPushing()
13929   -----------------------------------------------------------------------------
13930   check if diagonal input device direction results in pushing of object
13931   (by checking if the alternative direction is walkable, diggable, ...)
13932   =============================================================================
13933 */
13934
13935 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13936                                     int x, int y, int real_dx, int real_dy)
13937 {
13938   int jx, jy, dx, dy, xx, yy;
13939
13940   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13941     return TRUE;
13942
13943   // diagonal direction: check alternative direction
13944   jx = player->jx;
13945   jy = player->jy;
13946   dx = x - jx;
13947   dy = y - jy;
13948   xx = jx + (dx == 0 ? real_dx : 0);
13949   yy = jy + (dy == 0 ? real_dy : 0);
13950
13951   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13952 }
13953
13954 /*
13955   =============================================================================
13956   DigField()
13957   -----------------------------------------------------------------------------
13958   x, y:                 field next to player (non-diagonal) to try to dig to
13959   real_dx, real_dy:     direction as read from input device (can be diagonal)
13960   =============================================================================
13961 */
13962
13963 static int DigField(struct PlayerInfo *player,
13964                     int oldx, int oldy, int x, int y,
13965                     int real_dx, int real_dy, int mode)
13966 {
13967   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13968   boolean player_was_pushing = player->is_pushing;
13969   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13970   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13971   int jx = oldx, jy = oldy;
13972   int dx = x - jx, dy = y - jy;
13973   int nextx = x + dx, nexty = y + dy;
13974   int move_direction = (dx == -1 ? MV_LEFT  :
13975                         dx == +1 ? MV_RIGHT :
13976                         dy == -1 ? MV_UP    :
13977                         dy == +1 ? MV_DOWN  : MV_NONE);
13978   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13979   int dig_side = MV_DIR_OPPOSITE(move_direction);
13980   int old_element = Tile[jx][jy];
13981   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13982   int collect_count;
13983
13984   if (is_player)                // function can also be called by EL_PENGUIN
13985   {
13986     if (player->MovPos == 0)
13987     {
13988       player->is_digging = FALSE;
13989       player->is_collecting = FALSE;
13990     }
13991
13992     if (player->MovPos == 0)    // last pushing move finished
13993       player->is_pushing = FALSE;
13994
13995     if (mode == DF_NO_PUSH)     // player just stopped pushing
13996     {
13997       player->is_switching = FALSE;
13998       player->push_delay = -1;
13999
14000       return MP_NO_ACTION;
14001     }
14002   }
14003
14004   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14005     old_element = Back[jx][jy];
14006
14007   // in case of element dropped at player position, check background
14008   else if (Back[jx][jy] != EL_EMPTY &&
14009            game.engine_version >= VERSION_IDENT(2,2,0,0))
14010     old_element = Back[jx][jy];
14011
14012   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14013     return MP_NO_ACTION;        // field has no opening in this direction
14014
14015   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14016     return MP_NO_ACTION;        // field has no opening in this direction
14017
14018   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14019   {
14020     SplashAcid(x, y);
14021
14022     Tile[jx][jy] = player->artwork_element;
14023     InitMovingField(jx, jy, MV_DOWN);
14024     Store[jx][jy] = EL_ACID;
14025     ContinueMoving(jx, jy);
14026     BuryPlayer(player);
14027
14028     return MP_DONT_RUN_INTO;
14029   }
14030
14031   if (player_can_move && DONT_RUN_INTO(element))
14032   {
14033     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14034
14035     return MP_DONT_RUN_INTO;
14036   }
14037
14038   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14039     return MP_NO_ACTION;
14040
14041   collect_count = element_info[element].collect_count_initial;
14042
14043   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14044     return MP_NO_ACTION;
14045
14046   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14047     player_can_move = player_can_move_or_snap;
14048
14049   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14050       game.engine_version >= VERSION_IDENT(2,2,0,0))
14051   {
14052     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14053                                player->index_bit, dig_side);
14054     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14055                                         player->index_bit, dig_side);
14056
14057     if (element == EL_DC_LANDMINE)
14058       Bang(x, y);
14059
14060     if (Tile[x][y] != element)          // field changed by snapping
14061       return MP_ACTION;
14062
14063     return MP_NO_ACTION;
14064   }
14065
14066   if (player->gravity && is_player && !player->is_auto_moving &&
14067       canFallDown(player) && move_direction != MV_DOWN &&
14068       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14069     return MP_NO_ACTION;        // player cannot walk here due to gravity
14070
14071   if (player_can_move &&
14072       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14073   {
14074     int sound_element = SND_ELEMENT(element);
14075     int sound_action = ACTION_WALKING;
14076
14077     if (IS_RND_GATE(element))
14078     {
14079       if (!player->key[RND_GATE_NR(element)])
14080         return MP_NO_ACTION;
14081     }
14082     else if (IS_RND_GATE_GRAY(element))
14083     {
14084       if (!player->key[RND_GATE_GRAY_NR(element)])
14085         return MP_NO_ACTION;
14086     }
14087     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14088     {
14089       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14090         return MP_NO_ACTION;
14091     }
14092     else if (element == EL_EXIT_OPEN ||
14093              element == EL_EM_EXIT_OPEN ||
14094              element == EL_EM_EXIT_OPENING ||
14095              element == EL_STEEL_EXIT_OPEN ||
14096              element == EL_EM_STEEL_EXIT_OPEN ||
14097              element == EL_EM_STEEL_EXIT_OPENING ||
14098              element == EL_SP_EXIT_OPEN ||
14099              element == EL_SP_EXIT_OPENING)
14100     {
14101       sound_action = ACTION_PASSING;    // player is passing exit
14102     }
14103     else if (element == EL_EMPTY)
14104     {
14105       sound_action = ACTION_MOVING;             // nothing to walk on
14106     }
14107
14108     // play sound from background or player, whatever is available
14109     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14110       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14111     else
14112       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14113   }
14114   else if (player_can_move &&
14115            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14116   {
14117     if (!ACCESS_FROM(element, opposite_direction))
14118       return MP_NO_ACTION;      // field not accessible from this direction
14119
14120     if (CAN_MOVE(element))      // only fixed elements can be passed!
14121       return MP_NO_ACTION;
14122
14123     if (IS_EM_GATE(element))
14124     {
14125       if (!player->key[EM_GATE_NR(element)])
14126         return MP_NO_ACTION;
14127     }
14128     else if (IS_EM_GATE_GRAY(element))
14129     {
14130       if (!player->key[EM_GATE_GRAY_NR(element)])
14131         return MP_NO_ACTION;
14132     }
14133     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14134     {
14135       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14136         return MP_NO_ACTION;
14137     }
14138     else if (IS_EMC_GATE(element))
14139     {
14140       if (!player->key[EMC_GATE_NR(element)])
14141         return MP_NO_ACTION;
14142     }
14143     else if (IS_EMC_GATE_GRAY(element))
14144     {
14145       if (!player->key[EMC_GATE_GRAY_NR(element)])
14146         return MP_NO_ACTION;
14147     }
14148     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14149     {
14150       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14151         return MP_NO_ACTION;
14152     }
14153     else if (element == EL_DC_GATE_WHITE ||
14154              element == EL_DC_GATE_WHITE_GRAY ||
14155              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14156     {
14157       if (player->num_white_keys == 0)
14158         return MP_NO_ACTION;
14159
14160       player->num_white_keys--;
14161     }
14162     else if (IS_SP_PORT(element))
14163     {
14164       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14165           element == EL_SP_GRAVITY_PORT_RIGHT ||
14166           element == EL_SP_GRAVITY_PORT_UP ||
14167           element == EL_SP_GRAVITY_PORT_DOWN)
14168         player->gravity = !player->gravity;
14169       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14170                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14171                element == EL_SP_GRAVITY_ON_PORT_UP ||
14172                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14173         player->gravity = TRUE;
14174       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14175                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14176                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14177                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14178         player->gravity = FALSE;
14179     }
14180
14181     // automatically move to the next field with double speed
14182     player->programmed_action = move_direction;
14183
14184     if (player->move_delay_reset_counter == 0)
14185     {
14186       player->move_delay_reset_counter = 2;     // two double speed steps
14187
14188       DOUBLE_PLAYER_SPEED(player);
14189     }
14190
14191     PlayLevelSoundAction(x, y, ACTION_PASSING);
14192   }
14193   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14194   {
14195     RemoveField(x, y);
14196
14197     if (mode != DF_SNAP)
14198     {
14199       GfxElement[x][y] = GFX_ELEMENT(element);
14200       player->is_digging = TRUE;
14201     }
14202
14203     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14204
14205     // use old behaviour for old levels (digging)
14206     if (!level.finish_dig_collect)
14207     {
14208       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14209                                           player->index_bit, dig_side);
14210
14211       // if digging triggered player relocation, finish digging tile
14212       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14213         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14214     }
14215
14216     if (mode == DF_SNAP)
14217     {
14218       if (level.block_snap_field)
14219         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14220       else
14221         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14222
14223       // use old behaviour for old levels (snapping)
14224       if (!level.finish_dig_collect)
14225         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14226                                             player->index_bit, dig_side);
14227     }
14228   }
14229   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14230   {
14231     RemoveField(x, y);
14232
14233     if (is_player && mode != DF_SNAP)
14234     {
14235       GfxElement[x][y] = element;
14236       player->is_collecting = TRUE;
14237     }
14238
14239     if (element == EL_SPEED_PILL)
14240     {
14241       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14242     }
14243     else if (element == EL_EXTRA_TIME && level.time > 0)
14244     {
14245       TimeLeft += level.extra_time;
14246
14247       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14248
14249       DisplayGameControlValues();
14250     }
14251     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14252     {
14253       player->shield_normal_time_left += level.shield_normal_time;
14254       if (element == EL_SHIELD_DEADLY)
14255         player->shield_deadly_time_left += level.shield_deadly_time;
14256     }
14257     else if (element == EL_DYNAMITE ||
14258              element == EL_EM_DYNAMITE ||
14259              element == EL_SP_DISK_RED)
14260     {
14261       if (player->inventory_size < MAX_INVENTORY_SIZE)
14262         player->inventory_element[player->inventory_size++] = element;
14263
14264       DrawGameDoorValues();
14265     }
14266     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14267     {
14268       player->dynabomb_count++;
14269       player->dynabombs_left++;
14270     }
14271     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14272     {
14273       player->dynabomb_size++;
14274     }
14275     else if (element == EL_DYNABOMB_INCREASE_POWER)
14276     {
14277       player->dynabomb_xl = TRUE;
14278     }
14279     else if (IS_KEY(element))
14280     {
14281       player->key[KEY_NR(element)] = TRUE;
14282
14283       DrawGameDoorValues();
14284     }
14285     else if (element == EL_DC_KEY_WHITE)
14286     {
14287       player->num_white_keys++;
14288
14289       // display white keys?
14290       // DrawGameDoorValues();
14291     }
14292     else if (IS_ENVELOPE(element))
14293     {
14294       player->show_envelope = element;
14295     }
14296     else if (element == EL_EMC_LENSES)
14297     {
14298       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14299
14300       RedrawAllInvisibleElementsForLenses();
14301     }
14302     else if (element == EL_EMC_MAGNIFIER)
14303     {
14304       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14305
14306       RedrawAllInvisibleElementsForMagnifier();
14307     }
14308     else if (IS_DROPPABLE(element) ||
14309              IS_THROWABLE(element))     // can be collected and dropped
14310     {
14311       int i;
14312
14313       if (collect_count == 0)
14314         player->inventory_infinite_element = element;
14315       else
14316         for (i = 0; i < collect_count; i++)
14317           if (player->inventory_size < MAX_INVENTORY_SIZE)
14318             player->inventory_element[player->inventory_size++] = element;
14319
14320       DrawGameDoorValues();
14321     }
14322     else if (collect_count > 0)
14323     {
14324       game.gems_still_needed -= collect_count;
14325       if (game.gems_still_needed < 0)
14326         game.gems_still_needed = 0;
14327
14328       game.snapshot.collected_item = TRUE;
14329
14330       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14331
14332       DisplayGameControlValues();
14333     }
14334
14335     RaiseScoreElement(element);
14336     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14337
14338     // use old behaviour for old levels (collecting)
14339     if (!level.finish_dig_collect && is_player)
14340     {
14341       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14342                                           player->index_bit, dig_side);
14343
14344       // if collecting triggered player relocation, finish collecting tile
14345       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14346         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14347     }
14348
14349     if (mode == DF_SNAP)
14350     {
14351       if (level.block_snap_field)
14352         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14353       else
14354         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14355
14356       // use old behaviour for old levels (snapping)
14357       if (!level.finish_dig_collect)
14358         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14359                                             player->index_bit, dig_side);
14360     }
14361   }
14362   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14363   {
14364     if (mode == DF_SNAP && element != EL_BD_ROCK)
14365       return MP_NO_ACTION;
14366
14367     if (CAN_FALL(element) && dy)
14368       return MP_NO_ACTION;
14369
14370     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14371         !(element == EL_SPRING && level.use_spring_bug))
14372       return MP_NO_ACTION;
14373
14374     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14375         ((move_direction & MV_VERTICAL &&
14376           ((element_info[element].move_pattern & MV_LEFT &&
14377             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14378            (element_info[element].move_pattern & MV_RIGHT &&
14379             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14380          (move_direction & MV_HORIZONTAL &&
14381           ((element_info[element].move_pattern & MV_UP &&
14382             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14383            (element_info[element].move_pattern & MV_DOWN &&
14384             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14385       return MP_NO_ACTION;
14386
14387     // do not push elements already moving away faster than player
14388     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14389         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14390       return MP_NO_ACTION;
14391
14392     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14393     {
14394       if (player->push_delay_value == -1 || !player_was_pushing)
14395         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14396     }
14397     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14398     {
14399       if (player->push_delay_value == -1)
14400         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14401     }
14402     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14403     {
14404       if (!player->is_pushing)
14405         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14406     }
14407
14408     player->is_pushing = TRUE;
14409     player->is_active = TRUE;
14410
14411     if (!(IN_LEV_FIELD(nextx, nexty) &&
14412           (IS_FREE(nextx, nexty) ||
14413            (IS_SB_ELEMENT(element) &&
14414             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14415            (IS_CUSTOM_ELEMENT(element) &&
14416             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14417       return MP_NO_ACTION;
14418
14419     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14420       return MP_NO_ACTION;
14421
14422     if (player->push_delay == -1)       // new pushing; restart delay
14423       player->push_delay = 0;
14424
14425     if (player->push_delay < player->push_delay_value &&
14426         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14427         element != EL_SPRING && element != EL_BALLOON)
14428     {
14429       // make sure that there is no move delay before next try to push
14430       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14431         player->move_delay = 0;
14432
14433       return MP_NO_ACTION;
14434     }
14435
14436     if (IS_CUSTOM_ELEMENT(element) &&
14437         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14438     {
14439       if (!DigFieldByCE(nextx, nexty, element))
14440         return MP_NO_ACTION;
14441     }
14442
14443     if (IS_SB_ELEMENT(element))
14444     {
14445       boolean sokoban_task_solved = FALSE;
14446
14447       if (element == EL_SOKOBAN_FIELD_FULL)
14448       {
14449         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14450
14451         IncrementSokobanFieldsNeeded();
14452         IncrementSokobanObjectsNeeded();
14453       }
14454
14455       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14456       {
14457         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14458
14459         DecrementSokobanFieldsNeeded();
14460         DecrementSokobanObjectsNeeded();
14461
14462         // sokoban object was pushed from empty field to sokoban field
14463         if (Back[x][y] == EL_EMPTY)
14464           sokoban_task_solved = TRUE;
14465       }
14466
14467       Tile[x][y] = EL_SOKOBAN_OBJECT;
14468
14469       if (Back[x][y] == Back[nextx][nexty])
14470         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14471       else if (Back[x][y] != 0)
14472         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14473                                     ACTION_EMPTYING);
14474       else
14475         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14476                                     ACTION_FILLING);
14477
14478       if (sokoban_task_solved &&
14479           game.sokoban_fields_still_needed == 0 &&
14480           game.sokoban_objects_still_needed == 0 &&
14481           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14482       {
14483         game.players_still_needed = 0;
14484
14485         LevelSolved();
14486
14487         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14488       }
14489     }
14490     else
14491       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14492
14493     InitMovingField(x, y, move_direction);
14494     GfxAction[x][y] = ACTION_PUSHING;
14495
14496     if (mode == DF_SNAP)
14497       ContinueMoving(x, y);
14498     else
14499       MovPos[x][y] = (dx != 0 ? dx : dy);
14500
14501     Pushed[x][y] = TRUE;
14502     Pushed[nextx][nexty] = TRUE;
14503
14504     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14505       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14506     else
14507       player->push_delay_value = -1;    // get new value later
14508
14509     // check for element change _after_ element has been pushed
14510     if (game.use_change_when_pushing_bug)
14511     {
14512       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14513                                  player->index_bit, dig_side);
14514       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14515                                           player->index_bit, dig_side);
14516     }
14517   }
14518   else if (IS_SWITCHABLE(element))
14519   {
14520     if (PLAYER_SWITCHING(player, x, y))
14521     {
14522       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14523                                           player->index_bit, dig_side);
14524
14525       return MP_ACTION;
14526     }
14527
14528     player->is_switching = TRUE;
14529     player->switch_x = x;
14530     player->switch_y = y;
14531
14532     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14533
14534     if (element == EL_ROBOT_WHEEL)
14535     {
14536       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14537
14538       game.robot_wheel_x = x;
14539       game.robot_wheel_y = y;
14540       game.robot_wheel_active = TRUE;
14541
14542       TEST_DrawLevelField(x, y);
14543     }
14544     else if (element == EL_SP_TERMINAL)
14545     {
14546       int xx, yy;
14547
14548       SCAN_PLAYFIELD(xx, yy)
14549       {
14550         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14551         {
14552           Bang(xx, yy);
14553         }
14554         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14555         {
14556           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14557
14558           ResetGfxAnimation(xx, yy);
14559           TEST_DrawLevelField(xx, yy);
14560         }
14561       }
14562     }
14563     else if (IS_BELT_SWITCH(element))
14564     {
14565       ToggleBeltSwitch(x, y);
14566     }
14567     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14568              element == EL_SWITCHGATE_SWITCH_DOWN ||
14569              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14570              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14571     {
14572       ToggleSwitchgateSwitch(x, y);
14573     }
14574     else if (element == EL_LIGHT_SWITCH ||
14575              element == EL_LIGHT_SWITCH_ACTIVE)
14576     {
14577       ToggleLightSwitch(x, y);
14578     }
14579     else if (element == EL_TIMEGATE_SWITCH ||
14580              element == EL_DC_TIMEGATE_SWITCH)
14581     {
14582       ActivateTimegateSwitch(x, y);
14583     }
14584     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14585              element == EL_BALLOON_SWITCH_RIGHT ||
14586              element == EL_BALLOON_SWITCH_UP    ||
14587              element == EL_BALLOON_SWITCH_DOWN  ||
14588              element == EL_BALLOON_SWITCH_NONE  ||
14589              element == EL_BALLOON_SWITCH_ANY)
14590     {
14591       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14592                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14593                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14594                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14595                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14596                              move_direction);
14597     }
14598     else if (element == EL_LAMP)
14599     {
14600       Tile[x][y] = EL_LAMP_ACTIVE;
14601       game.lights_still_needed--;
14602
14603       ResetGfxAnimation(x, y);
14604       TEST_DrawLevelField(x, y);
14605     }
14606     else if (element == EL_TIME_ORB_FULL)
14607     {
14608       Tile[x][y] = EL_TIME_ORB_EMPTY;
14609
14610       if (level.time > 0 || level.use_time_orb_bug)
14611       {
14612         TimeLeft += level.time_orb_time;
14613         game.no_time_limit = FALSE;
14614
14615         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14616
14617         DisplayGameControlValues();
14618       }
14619
14620       ResetGfxAnimation(x, y);
14621       TEST_DrawLevelField(x, y);
14622     }
14623     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14624              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14625     {
14626       int xx, yy;
14627
14628       game.ball_active = !game.ball_active;
14629
14630       SCAN_PLAYFIELD(xx, yy)
14631       {
14632         int e = Tile[xx][yy];
14633
14634         if (game.ball_active)
14635         {
14636           if (e == EL_EMC_MAGIC_BALL)
14637             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14638           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14639             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14640         }
14641         else
14642         {
14643           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14644             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14645           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14646             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14647         }
14648       }
14649     }
14650
14651     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14652                                         player->index_bit, dig_side);
14653
14654     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14655                                         player->index_bit, dig_side);
14656
14657     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14658                                         player->index_bit, dig_side);
14659
14660     return MP_ACTION;
14661   }
14662   else
14663   {
14664     if (!PLAYER_SWITCHING(player, x, y))
14665     {
14666       player->is_switching = TRUE;
14667       player->switch_x = x;
14668       player->switch_y = y;
14669
14670       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14671                                  player->index_bit, dig_side);
14672       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14673                                           player->index_bit, dig_side);
14674
14675       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14676                                  player->index_bit, dig_side);
14677       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14678                                           player->index_bit, dig_side);
14679     }
14680
14681     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14682                                player->index_bit, dig_side);
14683     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14684                                         player->index_bit, dig_side);
14685
14686     return MP_NO_ACTION;
14687   }
14688
14689   player->push_delay = -1;
14690
14691   if (is_player)                // function can also be called by EL_PENGUIN
14692   {
14693     if (Tile[x][y] != element)          // really digged/collected something
14694     {
14695       player->is_collecting = !player->is_digging;
14696       player->is_active = TRUE;
14697
14698       player->last_removed_element = element;
14699     }
14700   }
14701
14702   return MP_MOVING;
14703 }
14704
14705 static boolean DigFieldByCE(int x, int y, int digging_element)
14706 {
14707   int element = Tile[x][y];
14708
14709   if (!IS_FREE(x, y))
14710   {
14711     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14712                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14713                   ACTION_BREAKING);
14714
14715     // no element can dig solid indestructible elements
14716     if (IS_INDESTRUCTIBLE(element) &&
14717         !IS_DIGGABLE(element) &&
14718         !IS_COLLECTIBLE(element))
14719       return FALSE;
14720
14721     if (AmoebaNr[x][y] &&
14722         (element == EL_AMOEBA_FULL ||
14723          element == EL_BD_AMOEBA ||
14724          element == EL_AMOEBA_GROWING))
14725     {
14726       AmoebaCnt[AmoebaNr[x][y]]--;
14727       AmoebaCnt2[AmoebaNr[x][y]]--;
14728     }
14729
14730     if (IS_MOVING(x, y))
14731       RemoveMovingField(x, y);
14732     else
14733     {
14734       RemoveField(x, y);
14735       TEST_DrawLevelField(x, y);
14736     }
14737
14738     // if digged element was about to explode, prevent the explosion
14739     ExplodeField[x][y] = EX_TYPE_NONE;
14740
14741     PlayLevelSoundAction(x, y, action);
14742   }
14743
14744   Store[x][y] = EL_EMPTY;
14745
14746   // this makes it possible to leave the removed element again
14747   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14748     Store[x][y] = element;
14749
14750   return TRUE;
14751 }
14752
14753 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14754 {
14755   int jx = player->jx, jy = player->jy;
14756   int x = jx + dx, y = jy + dy;
14757   int snap_direction = (dx == -1 ? MV_LEFT  :
14758                         dx == +1 ? MV_RIGHT :
14759                         dy == -1 ? MV_UP    :
14760                         dy == +1 ? MV_DOWN  : MV_NONE);
14761   boolean can_continue_snapping = (level.continuous_snapping &&
14762                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14763
14764   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14765     return FALSE;
14766
14767   if (!player->active || !IN_LEV_FIELD(x, y))
14768     return FALSE;
14769
14770   if (dx && dy)
14771     return FALSE;
14772
14773   if (!dx && !dy)
14774   {
14775     if (player->MovPos == 0)
14776       player->is_pushing = FALSE;
14777
14778     player->is_snapping = FALSE;
14779
14780     if (player->MovPos == 0)
14781     {
14782       player->is_moving = FALSE;
14783       player->is_digging = FALSE;
14784       player->is_collecting = FALSE;
14785     }
14786
14787     return FALSE;
14788   }
14789
14790   // prevent snapping with already pressed snap key when not allowed
14791   if (player->is_snapping && !can_continue_snapping)
14792     return FALSE;
14793
14794   player->MovDir = snap_direction;
14795
14796   if (player->MovPos == 0)
14797   {
14798     player->is_moving = FALSE;
14799     player->is_digging = FALSE;
14800     player->is_collecting = FALSE;
14801   }
14802
14803   player->is_dropping = FALSE;
14804   player->is_dropping_pressed = FALSE;
14805   player->drop_pressed_delay = 0;
14806
14807   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14808     return FALSE;
14809
14810   player->is_snapping = TRUE;
14811   player->is_active = TRUE;
14812
14813   if (player->MovPos == 0)
14814   {
14815     player->is_moving = FALSE;
14816     player->is_digging = FALSE;
14817     player->is_collecting = FALSE;
14818   }
14819
14820   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14821     TEST_DrawLevelField(player->last_jx, player->last_jy);
14822
14823   TEST_DrawLevelField(x, y);
14824
14825   return TRUE;
14826 }
14827
14828 static boolean DropElement(struct PlayerInfo *player)
14829 {
14830   int old_element, new_element;
14831   int dropx = player->jx, dropy = player->jy;
14832   int drop_direction = player->MovDir;
14833   int drop_side = drop_direction;
14834   int drop_element = get_next_dropped_element(player);
14835
14836   /* do not drop an element on top of another element; when holding drop key
14837      pressed without moving, dropped element must move away before the next
14838      element can be dropped (this is especially important if the next element
14839      is dynamite, which can be placed on background for historical reasons) */
14840   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14841     return MP_ACTION;
14842
14843   if (IS_THROWABLE(drop_element))
14844   {
14845     dropx += GET_DX_FROM_DIR(drop_direction);
14846     dropy += GET_DY_FROM_DIR(drop_direction);
14847
14848     if (!IN_LEV_FIELD(dropx, dropy))
14849       return FALSE;
14850   }
14851
14852   old_element = Tile[dropx][dropy];     // old element at dropping position
14853   new_element = drop_element;           // default: no change when dropping
14854
14855   // check if player is active, not moving and ready to drop
14856   if (!player->active || player->MovPos || player->drop_delay > 0)
14857     return FALSE;
14858
14859   // check if player has anything that can be dropped
14860   if (new_element == EL_UNDEFINED)
14861     return FALSE;
14862
14863   // only set if player has anything that can be dropped
14864   player->is_dropping_pressed = TRUE;
14865
14866   // check if drop key was pressed long enough for EM style dynamite
14867   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14868     return FALSE;
14869
14870   // check if anything can be dropped at the current position
14871   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14872     return FALSE;
14873
14874   // collected custom elements can only be dropped on empty fields
14875   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14876     return FALSE;
14877
14878   if (old_element != EL_EMPTY)
14879     Back[dropx][dropy] = old_element;   // store old element on this field
14880
14881   ResetGfxAnimation(dropx, dropy);
14882   ResetRandomAnimationValue(dropx, dropy);
14883
14884   if (player->inventory_size > 0 ||
14885       player->inventory_infinite_element != EL_UNDEFINED)
14886   {
14887     if (player->inventory_size > 0)
14888     {
14889       player->inventory_size--;
14890
14891       DrawGameDoorValues();
14892
14893       if (new_element == EL_DYNAMITE)
14894         new_element = EL_DYNAMITE_ACTIVE;
14895       else if (new_element == EL_EM_DYNAMITE)
14896         new_element = EL_EM_DYNAMITE_ACTIVE;
14897       else if (new_element == EL_SP_DISK_RED)
14898         new_element = EL_SP_DISK_RED_ACTIVE;
14899     }
14900
14901     Tile[dropx][dropy] = new_element;
14902
14903     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14904       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14905                           el2img(Tile[dropx][dropy]), 0);
14906
14907     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14908
14909     // needed if previous element just changed to "empty" in the last frame
14910     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14911
14912     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14913                                player->index_bit, drop_side);
14914     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14915                                         CE_PLAYER_DROPS_X,
14916                                         player->index_bit, drop_side);
14917
14918     TestIfElementTouchesCustomElement(dropx, dropy);
14919   }
14920   else          // player is dropping a dyna bomb
14921   {
14922     player->dynabombs_left--;
14923
14924     Tile[dropx][dropy] = new_element;
14925
14926     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14927       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14928                           el2img(Tile[dropx][dropy]), 0);
14929
14930     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14931   }
14932
14933   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14934     InitField_WithBug1(dropx, dropy, FALSE);
14935
14936   new_element = Tile[dropx][dropy];     // element might have changed
14937
14938   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14939       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14940   {
14941     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14942       MovDir[dropx][dropy] = drop_direction;
14943
14944     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14945
14946     // do not cause impact style collision by dropping elements that can fall
14947     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14948   }
14949
14950   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14951   player->is_dropping = TRUE;
14952
14953   player->drop_pressed_delay = 0;
14954   player->is_dropping_pressed = FALSE;
14955
14956   player->drop_x = dropx;
14957   player->drop_y = dropy;
14958
14959   return TRUE;
14960 }
14961
14962 // ----------------------------------------------------------------------------
14963 // game sound playing functions
14964 // ----------------------------------------------------------------------------
14965
14966 static int *loop_sound_frame = NULL;
14967 static int *loop_sound_volume = NULL;
14968
14969 void InitPlayLevelSound(void)
14970 {
14971   int num_sounds = getSoundListSize();
14972
14973   checked_free(loop_sound_frame);
14974   checked_free(loop_sound_volume);
14975
14976   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14977   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14978 }
14979
14980 static void PlayLevelSound(int x, int y, int nr)
14981 {
14982   int sx = SCREENX(x), sy = SCREENY(y);
14983   int volume, stereo_position;
14984   int max_distance = 8;
14985   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14986
14987   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14988       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14989     return;
14990
14991   if (!IN_LEV_FIELD(x, y) ||
14992       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14993       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14994     return;
14995
14996   volume = SOUND_MAX_VOLUME;
14997
14998   if (!IN_SCR_FIELD(sx, sy))
14999   {
15000     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15001     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15002
15003     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15004   }
15005
15006   stereo_position = (SOUND_MAX_LEFT +
15007                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15008                      (SCR_FIELDX + 2 * max_distance));
15009
15010   if (IS_LOOP_SOUND(nr))
15011   {
15012     /* This assures that quieter loop sounds do not overwrite louder ones,
15013        while restarting sound volume comparison with each new game frame. */
15014
15015     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15016       return;
15017
15018     loop_sound_volume[nr] = volume;
15019     loop_sound_frame[nr] = FrameCounter;
15020   }
15021
15022   PlaySoundExt(nr, volume, stereo_position, type);
15023 }
15024
15025 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15026 {
15027   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15028                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15029                  y < LEVELY(BY1) ? LEVELY(BY1) :
15030                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15031                  sound_action);
15032 }
15033
15034 static void PlayLevelSoundAction(int x, int y, int action)
15035 {
15036   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15037 }
15038
15039 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15040 {
15041   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15042
15043   if (sound_effect != SND_UNDEFINED)
15044     PlayLevelSound(x, y, sound_effect);
15045 }
15046
15047 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15048                                               int action)
15049 {
15050   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15051
15052   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15053     PlayLevelSound(x, y, sound_effect);
15054 }
15055
15056 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15057 {
15058   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15059
15060   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15061     PlayLevelSound(x, y, sound_effect);
15062 }
15063
15064 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15065 {
15066   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15067
15068   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15069     StopSound(sound_effect);
15070 }
15071
15072 static int getLevelMusicNr(void)
15073 {
15074   if (levelset.music[level_nr] != MUS_UNDEFINED)
15075     return levelset.music[level_nr];            // from config file
15076   else
15077     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15078 }
15079
15080 static void FadeLevelSounds(void)
15081 {
15082   FadeSounds();
15083 }
15084
15085 static void FadeLevelMusic(void)
15086 {
15087   int music_nr = getLevelMusicNr();
15088   char *curr_music = getCurrentlyPlayingMusicFilename();
15089   char *next_music = getMusicInfoEntryFilename(music_nr);
15090
15091   if (!strEqual(curr_music, next_music))
15092     FadeMusic();
15093 }
15094
15095 void FadeLevelSoundsAndMusic(void)
15096 {
15097   FadeLevelSounds();
15098   FadeLevelMusic();
15099 }
15100
15101 static void PlayLevelMusic(void)
15102 {
15103   int music_nr = getLevelMusicNr();
15104   char *curr_music = getCurrentlyPlayingMusicFilename();
15105   char *next_music = getMusicInfoEntryFilename(music_nr);
15106
15107   if (!strEqual(curr_music, next_music))
15108     PlayMusicLoop(music_nr);
15109 }
15110
15111 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15112 {
15113   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15114   int offset = 0;
15115   int x = xx - offset;
15116   int y = yy - offset;
15117
15118   switch (sample)
15119   {
15120     case SOUND_blank:
15121       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15122       break;
15123
15124     case SOUND_roll:
15125       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15126       break;
15127
15128     case SOUND_stone:
15129       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15130       break;
15131
15132     case SOUND_nut:
15133       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15134       break;
15135
15136     case SOUND_crack:
15137       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15138       break;
15139
15140     case SOUND_bug:
15141       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15142       break;
15143
15144     case SOUND_tank:
15145       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15146       break;
15147
15148     case SOUND_android_clone:
15149       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15150       break;
15151
15152     case SOUND_android_move:
15153       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15154       break;
15155
15156     case SOUND_spring:
15157       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15158       break;
15159
15160     case SOUND_slurp:
15161       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15162       break;
15163
15164     case SOUND_eater:
15165       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15166       break;
15167
15168     case SOUND_eater_eat:
15169       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15170       break;
15171
15172     case SOUND_alien:
15173       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15174       break;
15175
15176     case SOUND_collect:
15177       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15178       break;
15179
15180     case SOUND_diamond:
15181       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15182       break;
15183
15184     case SOUND_squash:
15185       // !!! CHECK THIS !!!
15186 #if 1
15187       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15188 #else
15189       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15190 #endif
15191       break;
15192
15193     case SOUND_wonderfall:
15194       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15195       break;
15196
15197     case SOUND_drip:
15198       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15199       break;
15200
15201     case SOUND_push:
15202       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15203       break;
15204
15205     case SOUND_dirt:
15206       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15207       break;
15208
15209     case SOUND_acid:
15210       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15211       break;
15212
15213     case SOUND_ball:
15214       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15215       break;
15216
15217     case SOUND_slide:
15218       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15219       break;
15220
15221     case SOUND_wonder:
15222       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15223       break;
15224
15225     case SOUND_door:
15226       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15227       break;
15228
15229     case SOUND_exit_open:
15230       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15231       break;
15232
15233     case SOUND_exit_leave:
15234       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15235       break;
15236
15237     case SOUND_dynamite:
15238       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15239       break;
15240
15241     case SOUND_tick:
15242       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15243       break;
15244
15245     case SOUND_press:
15246       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15247       break;
15248
15249     case SOUND_wheel:
15250       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15251       break;
15252
15253     case SOUND_boom:
15254       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15255       break;
15256
15257     case SOUND_die:
15258       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15259       break;
15260
15261     case SOUND_time:
15262       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15263       break;
15264
15265     default:
15266       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15267       break;
15268   }
15269 }
15270
15271 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15272 {
15273   int element = map_element_SP_to_RND(element_sp);
15274   int action = map_action_SP_to_RND(action_sp);
15275   int offset = (setup.sp_show_border_elements ? 0 : 1);
15276   int x = xx - offset;
15277   int y = yy - offset;
15278
15279   PlayLevelSoundElementAction(x, y, element, action);
15280 }
15281
15282 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15283 {
15284   int element = map_element_MM_to_RND(element_mm);
15285   int action = map_action_MM_to_RND(action_mm);
15286   int offset = 0;
15287   int x = xx - offset;
15288   int y = yy - offset;
15289
15290   if (!IS_MM_ELEMENT(element))
15291     element = EL_MM_DEFAULT;
15292
15293   PlayLevelSoundElementAction(x, y, element, action);
15294 }
15295
15296 void PlaySound_MM(int sound_mm)
15297 {
15298   int sound = map_sound_MM_to_RND(sound_mm);
15299
15300   if (sound == SND_UNDEFINED)
15301     return;
15302
15303   PlaySound(sound);
15304 }
15305
15306 void PlaySoundLoop_MM(int sound_mm)
15307 {
15308   int sound = map_sound_MM_to_RND(sound_mm);
15309
15310   if (sound == SND_UNDEFINED)
15311     return;
15312
15313   PlaySoundLoop(sound);
15314 }
15315
15316 void StopSound_MM(int sound_mm)
15317 {
15318   int sound = map_sound_MM_to_RND(sound_mm);
15319
15320   if (sound == SND_UNDEFINED)
15321     return;
15322
15323   StopSound(sound);
15324 }
15325
15326 void RaiseScore(int value)
15327 {
15328   game.score += value;
15329
15330   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15331
15332   DisplayGameControlValues();
15333 }
15334
15335 void RaiseScoreElement(int element)
15336 {
15337   switch (element)
15338   {
15339     case EL_EMERALD:
15340     case EL_BD_DIAMOND:
15341     case EL_EMERALD_YELLOW:
15342     case EL_EMERALD_RED:
15343     case EL_EMERALD_PURPLE:
15344     case EL_SP_INFOTRON:
15345       RaiseScore(level.score[SC_EMERALD]);
15346       break;
15347     case EL_DIAMOND:
15348       RaiseScore(level.score[SC_DIAMOND]);
15349       break;
15350     case EL_CRYSTAL:
15351       RaiseScore(level.score[SC_CRYSTAL]);
15352       break;
15353     case EL_PEARL:
15354       RaiseScore(level.score[SC_PEARL]);
15355       break;
15356     case EL_BUG:
15357     case EL_BD_BUTTERFLY:
15358     case EL_SP_ELECTRON:
15359       RaiseScore(level.score[SC_BUG]);
15360       break;
15361     case EL_SPACESHIP:
15362     case EL_BD_FIREFLY:
15363     case EL_SP_SNIKSNAK:
15364       RaiseScore(level.score[SC_SPACESHIP]);
15365       break;
15366     case EL_YAMYAM:
15367     case EL_DARK_YAMYAM:
15368       RaiseScore(level.score[SC_YAMYAM]);
15369       break;
15370     case EL_ROBOT:
15371       RaiseScore(level.score[SC_ROBOT]);
15372       break;
15373     case EL_PACMAN:
15374       RaiseScore(level.score[SC_PACMAN]);
15375       break;
15376     case EL_NUT:
15377       RaiseScore(level.score[SC_NUT]);
15378       break;
15379     case EL_DYNAMITE:
15380     case EL_EM_DYNAMITE:
15381     case EL_SP_DISK_RED:
15382     case EL_DYNABOMB_INCREASE_NUMBER:
15383     case EL_DYNABOMB_INCREASE_SIZE:
15384     case EL_DYNABOMB_INCREASE_POWER:
15385       RaiseScore(level.score[SC_DYNAMITE]);
15386       break;
15387     case EL_SHIELD_NORMAL:
15388     case EL_SHIELD_DEADLY:
15389       RaiseScore(level.score[SC_SHIELD]);
15390       break;
15391     case EL_EXTRA_TIME:
15392       RaiseScore(level.extra_time_score);
15393       break;
15394     case EL_KEY_1:
15395     case EL_KEY_2:
15396     case EL_KEY_3:
15397     case EL_KEY_4:
15398     case EL_EM_KEY_1:
15399     case EL_EM_KEY_2:
15400     case EL_EM_KEY_3:
15401     case EL_EM_KEY_4:
15402     case EL_EMC_KEY_5:
15403     case EL_EMC_KEY_6:
15404     case EL_EMC_KEY_7:
15405     case EL_EMC_KEY_8:
15406     case EL_DC_KEY_WHITE:
15407       RaiseScore(level.score[SC_KEY]);
15408       break;
15409     default:
15410       RaiseScore(element_info[element].collect_score);
15411       break;
15412   }
15413 }
15414
15415 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15416 {
15417   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15418   {
15419     if (!quick_quit)
15420     {
15421       // prevent short reactivation of overlay buttons while closing door
15422       SetOverlayActive(FALSE);
15423
15424       // door may still be open due to skipped or envelope style request
15425       CloseDoor(DOOR_CLOSE_1);
15426     }
15427
15428     if (network.enabled)
15429       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15430     else
15431     {
15432       if (quick_quit)
15433         FadeSkipNextFadeIn();
15434
15435       SetGameStatus(GAME_MODE_MAIN);
15436
15437       DrawMainMenu();
15438     }
15439   }
15440   else          // continue playing the game
15441   {
15442     if (tape.playing && tape.deactivate_display)
15443       TapeDeactivateDisplayOff(TRUE);
15444
15445     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15446
15447     if (tape.playing && tape.deactivate_display)
15448       TapeDeactivateDisplayOn();
15449   }
15450 }
15451
15452 void RequestQuitGame(boolean escape_key_pressed)
15453 {
15454   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15455   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15456                         level_editor_test_game);
15457   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15458                           quick_quit);
15459
15460   RequestQuitGameExt(skip_request, quick_quit,
15461                      "Do you really want to quit the game?");
15462 }
15463
15464 void RequestRestartGame(char *message)
15465 {
15466   game.restart_game_message = NULL;
15467
15468   boolean has_started_game = hasStartedNetworkGame();
15469   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15470
15471   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15472   {
15473     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15474   }
15475   else
15476   {
15477     // needed in case of envelope request to close game panel
15478     CloseDoor(DOOR_CLOSE_1);
15479
15480     SetGameStatus(GAME_MODE_MAIN);
15481
15482     DrawMainMenu();
15483   }
15484 }
15485
15486 void CheckGameOver(void)
15487 {
15488   static boolean last_game_over = FALSE;
15489   static int game_over_delay = 0;
15490   int game_over_delay_value = 50;
15491   boolean game_over = checkGameFailed();
15492
15493   // do not handle game over if request dialog is already active
15494   if (game.request_active)
15495     return;
15496
15497   // do not ask to play again if game was never actually played
15498   if (!game.GamePlayed)
15499     return;
15500
15501   if (!game_over)
15502   {
15503     last_game_over = FALSE;
15504     game_over_delay = game_over_delay_value;
15505
15506     return;
15507   }
15508
15509   if (game_over_delay > 0)
15510   {
15511     game_over_delay--;
15512
15513     return;
15514   }
15515
15516   if (last_game_over != game_over)
15517     game.restart_game_message = (hasStartedNetworkGame() ?
15518                                  "Game over! Play it again?" :
15519                                  "Game over!");
15520
15521   last_game_over = game_over;
15522 }
15523
15524 boolean checkGameSolved(void)
15525 {
15526   // set for all game engines if level was solved
15527   return game.LevelSolved_GameEnd;
15528 }
15529
15530 boolean checkGameFailed(void)
15531 {
15532   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15533     return (game_em.game_over && !game_em.level_solved);
15534   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15535     return (game_sp.game_over && !game_sp.level_solved);
15536   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15537     return (game_mm.game_over && !game_mm.level_solved);
15538   else                          // GAME_ENGINE_TYPE_RND
15539     return (game.GameOver && !game.LevelSolved);
15540 }
15541
15542 boolean checkGameEnded(void)
15543 {
15544   return (checkGameSolved() || checkGameFailed());
15545 }
15546
15547
15548 // ----------------------------------------------------------------------------
15549 // random generator functions
15550 // ----------------------------------------------------------------------------
15551
15552 unsigned int InitEngineRandom_RND(int seed)
15553 {
15554   game.num_random_calls = 0;
15555
15556   return InitEngineRandom(seed);
15557 }
15558
15559 unsigned int RND(int max)
15560 {
15561   if (max > 0)
15562   {
15563     game.num_random_calls++;
15564
15565     return GetEngineRandom(max);
15566   }
15567
15568   return 0;
15569 }
15570
15571
15572 // ----------------------------------------------------------------------------
15573 // game engine snapshot handling functions
15574 // ----------------------------------------------------------------------------
15575
15576 struct EngineSnapshotInfo
15577 {
15578   // runtime values for custom element collect score
15579   int collect_score[NUM_CUSTOM_ELEMENTS];
15580
15581   // runtime values for group element choice position
15582   int choice_pos[NUM_GROUP_ELEMENTS];
15583
15584   // runtime values for belt position animations
15585   int belt_graphic[4][NUM_BELT_PARTS];
15586   int belt_anim_mode[4][NUM_BELT_PARTS];
15587 };
15588
15589 static struct EngineSnapshotInfo engine_snapshot_rnd;
15590 static char *snapshot_level_identifier = NULL;
15591 static int snapshot_level_nr = -1;
15592
15593 static void SaveEngineSnapshotValues_RND(void)
15594 {
15595   static int belt_base_active_element[4] =
15596   {
15597     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15598     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15599     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15600     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15601   };
15602   int i, j;
15603
15604   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15605   {
15606     int element = EL_CUSTOM_START + i;
15607
15608     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15609   }
15610
15611   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15612   {
15613     int element = EL_GROUP_START + i;
15614
15615     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15616   }
15617
15618   for (i = 0; i < 4; i++)
15619   {
15620     for (j = 0; j < NUM_BELT_PARTS; j++)
15621     {
15622       int element = belt_base_active_element[i] + j;
15623       int graphic = el2img(element);
15624       int anim_mode = graphic_info[graphic].anim_mode;
15625
15626       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15627       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15628     }
15629   }
15630 }
15631
15632 static void LoadEngineSnapshotValues_RND(void)
15633 {
15634   unsigned int num_random_calls = game.num_random_calls;
15635   int i, j;
15636
15637   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15638   {
15639     int element = EL_CUSTOM_START + i;
15640
15641     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15642   }
15643
15644   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15645   {
15646     int element = EL_GROUP_START + i;
15647
15648     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15649   }
15650
15651   for (i = 0; i < 4; i++)
15652   {
15653     for (j = 0; j < NUM_BELT_PARTS; j++)
15654     {
15655       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15656       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15657
15658       graphic_info[graphic].anim_mode = anim_mode;
15659     }
15660   }
15661
15662   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15663   {
15664     InitRND(tape.random_seed);
15665     for (i = 0; i < num_random_calls; i++)
15666       RND(1);
15667   }
15668
15669   if (game.num_random_calls != num_random_calls)
15670   {
15671     Error("number of random calls out of sync");
15672     Error("number of random calls should be %d", num_random_calls);
15673     Error("number of random calls is %d", game.num_random_calls);
15674
15675     Fail("this should not happen -- please debug");
15676   }
15677 }
15678
15679 void FreeEngineSnapshotSingle(void)
15680 {
15681   FreeSnapshotSingle();
15682
15683   setString(&snapshot_level_identifier, NULL);
15684   snapshot_level_nr = -1;
15685 }
15686
15687 void FreeEngineSnapshotList(void)
15688 {
15689   FreeSnapshotList();
15690 }
15691
15692 static ListNode *SaveEngineSnapshotBuffers(void)
15693 {
15694   ListNode *buffers = NULL;
15695
15696   // copy some special values to a structure better suited for the snapshot
15697
15698   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15699     SaveEngineSnapshotValues_RND();
15700   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15701     SaveEngineSnapshotValues_EM();
15702   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15703     SaveEngineSnapshotValues_SP(&buffers);
15704   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15705     SaveEngineSnapshotValues_MM(&buffers);
15706
15707   // save values stored in special snapshot structure
15708
15709   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15710     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15711   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15712     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15713   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15714     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15715   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15716     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15717
15718   // save further RND engine values
15719
15720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15721   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15722   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15723
15724   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15725   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15726   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15727   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15728   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15729
15730   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15731   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15732   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15733
15734   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15735
15736   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15737   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15738
15739   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15740   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15741   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15742   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15743   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15744   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15745   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15746   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15747   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15748   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15749   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15750   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15751   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15752   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15753   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15754   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15755   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15756   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15757
15758   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15759   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15760
15761   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15762   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15763   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15764
15765   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15766   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15767
15768   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15769   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15770   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15771   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15772   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15773
15774   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15775   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15776
15777 #if 0
15778   ListNode *node = engine_snapshot_list_rnd;
15779   int num_bytes = 0;
15780
15781   while (node != NULL)
15782   {
15783     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15784
15785     node = node->next;
15786   }
15787
15788   Debug("game:playing:SaveEngineSnapshotBuffers",
15789         "size of engine snapshot: %d bytes", num_bytes);
15790 #endif
15791
15792   return buffers;
15793 }
15794
15795 void SaveEngineSnapshotSingle(void)
15796 {
15797   ListNode *buffers = SaveEngineSnapshotBuffers();
15798
15799   // finally save all snapshot buffers to single snapshot
15800   SaveSnapshotSingle(buffers);
15801
15802   // save level identification information
15803   setString(&snapshot_level_identifier, leveldir_current->identifier);
15804   snapshot_level_nr = level_nr;
15805 }
15806
15807 boolean CheckSaveEngineSnapshotToList(void)
15808 {
15809   boolean save_snapshot =
15810     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15811      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15812       game.snapshot.changed_action) ||
15813      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15814       game.snapshot.collected_item));
15815
15816   game.snapshot.changed_action = FALSE;
15817   game.snapshot.collected_item = FALSE;
15818   game.snapshot.save_snapshot = save_snapshot;
15819
15820   return save_snapshot;
15821 }
15822
15823 void SaveEngineSnapshotToList(void)
15824 {
15825   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15826       tape.quick_resume)
15827     return;
15828
15829   ListNode *buffers = SaveEngineSnapshotBuffers();
15830
15831   // finally save all snapshot buffers to snapshot list
15832   SaveSnapshotToList(buffers);
15833 }
15834
15835 void SaveEngineSnapshotToListInitial(void)
15836 {
15837   FreeEngineSnapshotList();
15838
15839   SaveEngineSnapshotToList();
15840 }
15841
15842 static void LoadEngineSnapshotValues(void)
15843 {
15844   // restore special values from snapshot structure
15845
15846   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15847     LoadEngineSnapshotValues_RND();
15848   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15849     LoadEngineSnapshotValues_EM();
15850   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15851     LoadEngineSnapshotValues_SP();
15852   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15853     LoadEngineSnapshotValues_MM();
15854 }
15855
15856 void LoadEngineSnapshotSingle(void)
15857 {
15858   LoadSnapshotSingle();
15859
15860   LoadEngineSnapshotValues();
15861 }
15862
15863 static void LoadEngineSnapshot_Undo(int steps)
15864 {
15865   LoadSnapshotFromList_Older(steps);
15866
15867   LoadEngineSnapshotValues();
15868 }
15869
15870 static void LoadEngineSnapshot_Redo(int steps)
15871 {
15872   LoadSnapshotFromList_Newer(steps);
15873
15874   LoadEngineSnapshotValues();
15875 }
15876
15877 boolean CheckEngineSnapshotSingle(void)
15878 {
15879   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15880           snapshot_level_nr == level_nr);
15881 }
15882
15883 boolean CheckEngineSnapshotList(void)
15884 {
15885   return CheckSnapshotList();
15886 }
15887
15888
15889 // ---------- new game button stuff -------------------------------------------
15890
15891 static struct
15892 {
15893   int graphic;
15894   struct XY *pos;
15895   int gadget_id;
15896   boolean *setup_value;
15897   boolean allowed_on_tape;
15898   boolean is_touch_button;
15899   char *infotext;
15900 } gamebutton_info[NUM_GAME_BUTTONS] =
15901 {
15902   {
15903     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15904     GAME_CTRL_ID_STOP,                          NULL,
15905     TRUE, FALSE,                                "stop game"
15906   },
15907   {
15908     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15909     GAME_CTRL_ID_PAUSE,                         NULL,
15910     TRUE, FALSE,                                "pause game"
15911   },
15912   {
15913     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15914     GAME_CTRL_ID_PLAY,                          NULL,
15915     TRUE, FALSE,                                "play game"
15916   },
15917   {
15918     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15919     GAME_CTRL_ID_UNDO,                          NULL,
15920     TRUE, FALSE,                                "undo step"
15921   },
15922   {
15923     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15924     GAME_CTRL_ID_REDO,                          NULL,
15925     TRUE, FALSE,                                "redo step"
15926   },
15927   {
15928     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15929     GAME_CTRL_ID_SAVE,                          NULL,
15930     TRUE, FALSE,                                "save game"
15931   },
15932   {
15933     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15934     GAME_CTRL_ID_PAUSE2,                        NULL,
15935     TRUE, FALSE,                                "pause game"
15936   },
15937   {
15938     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15939     GAME_CTRL_ID_LOAD,                          NULL,
15940     TRUE, FALSE,                                "load game"
15941   },
15942   {
15943     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15944     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15945     FALSE, FALSE,                               "stop game"
15946   },
15947   {
15948     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15949     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15950     FALSE, FALSE,                               "pause game"
15951   },
15952   {
15953     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15954     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15955     FALSE, FALSE,                               "play game"
15956   },
15957   {
15958     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15959     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15960     FALSE, TRUE,                                "stop game"
15961   },
15962   {
15963     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15964     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15965     FALSE, TRUE,                                "pause game"
15966   },
15967   {
15968     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15969     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15970     TRUE, FALSE,                                "background music on/off"
15971   },
15972   {
15973     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15974     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15975     TRUE, FALSE,                                "sound loops on/off"
15976   },
15977   {
15978     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15979     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15980     TRUE, FALSE,                                "normal sounds on/off"
15981   },
15982   {
15983     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15984     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15985     FALSE, FALSE,                               "background music on/off"
15986   },
15987   {
15988     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15989     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15990     FALSE, FALSE,                               "sound loops on/off"
15991   },
15992   {
15993     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15994     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15995     FALSE, FALSE,                               "normal sounds on/off"
15996   }
15997 };
15998
15999 void CreateGameButtons(void)
16000 {
16001   int i;
16002
16003   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16004   {
16005     int graphic = gamebutton_info[i].graphic;
16006     struct GraphicInfo *gfx = &graphic_info[graphic];
16007     struct XY *pos = gamebutton_info[i].pos;
16008     struct GadgetInfo *gi;
16009     int button_type;
16010     boolean checked;
16011     unsigned int event_mask;
16012     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16013     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16014     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16015     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16016     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16017     int gd_x   = gfx->src_x;
16018     int gd_y   = gfx->src_y;
16019     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16020     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16021     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16022     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16023     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16024     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16025     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16026     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16027     int id = i;
16028
16029     if (gfx->bitmap == NULL)
16030     {
16031       game_gadget[id] = NULL;
16032
16033       continue;
16034     }
16035
16036     if (id == GAME_CTRL_ID_STOP ||
16037         id == GAME_CTRL_ID_PANEL_STOP ||
16038         id == GAME_CTRL_ID_TOUCH_STOP ||
16039         id == GAME_CTRL_ID_PLAY ||
16040         id == GAME_CTRL_ID_PANEL_PLAY ||
16041         id == GAME_CTRL_ID_SAVE ||
16042         id == GAME_CTRL_ID_LOAD)
16043     {
16044       button_type = GD_TYPE_NORMAL_BUTTON;
16045       checked = FALSE;
16046       event_mask = GD_EVENT_RELEASED;
16047     }
16048     else if (id == GAME_CTRL_ID_UNDO ||
16049              id == GAME_CTRL_ID_REDO)
16050     {
16051       button_type = GD_TYPE_NORMAL_BUTTON;
16052       checked = FALSE;
16053       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16054     }
16055     else
16056     {
16057       button_type = GD_TYPE_CHECK_BUTTON;
16058       checked = (gamebutton_info[i].setup_value != NULL ?
16059                  *gamebutton_info[i].setup_value : FALSE);
16060       event_mask = GD_EVENT_PRESSED;
16061     }
16062
16063     gi = CreateGadget(GDI_CUSTOM_ID, id,
16064                       GDI_IMAGE_ID, graphic,
16065                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16066                       GDI_X, base_x + x,
16067                       GDI_Y, base_y + y,
16068                       GDI_WIDTH, gfx->width,
16069                       GDI_HEIGHT, gfx->height,
16070                       GDI_TYPE, button_type,
16071                       GDI_STATE, GD_BUTTON_UNPRESSED,
16072                       GDI_CHECKED, checked,
16073                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16074                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16075                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16076                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16077                       GDI_DIRECT_DRAW, FALSE,
16078                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16079                       GDI_EVENT_MASK, event_mask,
16080                       GDI_CALLBACK_ACTION, HandleGameButtons,
16081                       GDI_END);
16082
16083     if (gi == NULL)
16084       Fail("cannot create gadget");
16085
16086     game_gadget[id] = gi;
16087   }
16088 }
16089
16090 void FreeGameButtons(void)
16091 {
16092   int i;
16093
16094   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16095     FreeGadget(game_gadget[i]);
16096 }
16097
16098 static void UnmapGameButtonsAtSamePosition(int id)
16099 {
16100   int i;
16101
16102   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16103     if (i != id &&
16104         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16105         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16106       UnmapGadget(game_gadget[i]);
16107 }
16108
16109 static void UnmapGameButtonsAtSamePosition_All(void)
16110 {
16111   if (setup.show_snapshot_buttons)
16112   {
16113     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16114     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16115     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16116   }
16117   else
16118   {
16119     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16120     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16121     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16122
16123     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16124     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16125     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16126   }
16127 }
16128
16129 static void MapGameButtonsAtSamePosition(int id)
16130 {
16131   int i;
16132
16133   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16134     if (i != id &&
16135         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16136         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16137       MapGadget(game_gadget[i]);
16138
16139   UnmapGameButtonsAtSamePosition_All();
16140 }
16141
16142 void MapUndoRedoButtons(void)
16143 {
16144   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16145   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16146
16147   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16148   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16149 }
16150
16151 void UnmapUndoRedoButtons(void)
16152 {
16153   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16154   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16155
16156   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16157   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16158 }
16159
16160 void ModifyPauseButtons(void)
16161 {
16162   static int ids[] =
16163   {
16164     GAME_CTRL_ID_PAUSE,
16165     GAME_CTRL_ID_PAUSE2,
16166     GAME_CTRL_ID_PANEL_PAUSE,
16167     GAME_CTRL_ID_TOUCH_PAUSE,
16168     -1
16169   };
16170   int i;
16171
16172   for (i = 0; ids[i] > -1; i++)
16173     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16174 }
16175
16176 static void MapGameButtonsExt(boolean on_tape)
16177 {
16178   int i;
16179
16180   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16181     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16182         i != GAME_CTRL_ID_UNDO &&
16183         i != GAME_CTRL_ID_REDO)
16184       MapGadget(game_gadget[i]);
16185
16186   UnmapGameButtonsAtSamePosition_All();
16187
16188   RedrawGameButtons();
16189 }
16190
16191 static void UnmapGameButtonsExt(boolean on_tape)
16192 {
16193   int i;
16194
16195   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16196     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16197       UnmapGadget(game_gadget[i]);
16198 }
16199
16200 static void RedrawGameButtonsExt(boolean on_tape)
16201 {
16202   int i;
16203
16204   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16205     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16206       RedrawGadget(game_gadget[i]);
16207 }
16208
16209 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16210 {
16211   if (gi == NULL)
16212     return;
16213
16214   gi->checked = state;
16215 }
16216
16217 static void RedrawSoundButtonGadget(int id)
16218 {
16219   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16220              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16221              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16222              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16223              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16224              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16225              id);
16226
16227   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16228   RedrawGadget(game_gadget[id2]);
16229 }
16230
16231 void MapGameButtons(void)
16232 {
16233   MapGameButtonsExt(FALSE);
16234 }
16235
16236 void UnmapGameButtons(void)
16237 {
16238   UnmapGameButtonsExt(FALSE);
16239 }
16240
16241 void RedrawGameButtons(void)
16242 {
16243   RedrawGameButtonsExt(FALSE);
16244 }
16245
16246 void MapGameButtonsOnTape(void)
16247 {
16248   MapGameButtonsExt(TRUE);
16249 }
16250
16251 void UnmapGameButtonsOnTape(void)
16252 {
16253   UnmapGameButtonsExt(TRUE);
16254 }
16255
16256 void RedrawGameButtonsOnTape(void)
16257 {
16258   RedrawGameButtonsExt(TRUE);
16259 }
16260
16261 static void GameUndoRedoExt(void)
16262 {
16263   ClearPlayerAction();
16264
16265   tape.pausing = TRUE;
16266
16267   RedrawPlayfield();
16268   UpdateAndDisplayGameControlValues();
16269
16270   DrawCompleteVideoDisplay();
16271   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16272   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16273   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16274
16275   BackToFront();
16276 }
16277
16278 static void GameUndo(int steps)
16279 {
16280   if (!CheckEngineSnapshotList())
16281     return;
16282
16283   LoadEngineSnapshot_Undo(steps);
16284
16285   GameUndoRedoExt();
16286 }
16287
16288 static void GameRedo(int steps)
16289 {
16290   if (!CheckEngineSnapshotList())
16291     return;
16292
16293   LoadEngineSnapshot_Redo(steps);
16294
16295   GameUndoRedoExt();
16296 }
16297
16298 static void HandleGameButtonsExt(int id, int button)
16299 {
16300   static boolean game_undo_executed = FALSE;
16301   int steps = BUTTON_STEPSIZE(button);
16302   boolean handle_game_buttons =
16303     (game_status == GAME_MODE_PLAYING ||
16304      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16305
16306   if (!handle_game_buttons)
16307     return;
16308
16309   switch (id)
16310   {
16311     case GAME_CTRL_ID_STOP:
16312     case GAME_CTRL_ID_PANEL_STOP:
16313     case GAME_CTRL_ID_TOUCH_STOP:
16314       if (game_status == GAME_MODE_MAIN)
16315         break;
16316
16317       if (tape.playing)
16318         TapeStop();
16319       else
16320         RequestQuitGame(FALSE);
16321
16322       break;
16323
16324     case GAME_CTRL_ID_PAUSE:
16325     case GAME_CTRL_ID_PAUSE2:
16326     case GAME_CTRL_ID_PANEL_PAUSE:
16327     case GAME_CTRL_ID_TOUCH_PAUSE:
16328       if (network.enabled && game_status == GAME_MODE_PLAYING)
16329       {
16330         if (tape.pausing)
16331           SendToServer_ContinuePlaying();
16332         else
16333           SendToServer_PausePlaying();
16334       }
16335       else
16336         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16337
16338       game_undo_executed = FALSE;
16339
16340       break;
16341
16342     case GAME_CTRL_ID_PLAY:
16343     case GAME_CTRL_ID_PANEL_PLAY:
16344       if (game_status == GAME_MODE_MAIN)
16345       {
16346         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16347       }
16348       else if (tape.pausing)
16349       {
16350         if (network.enabled)
16351           SendToServer_ContinuePlaying();
16352         else
16353           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16354       }
16355       break;
16356
16357     case GAME_CTRL_ID_UNDO:
16358       // Important: When using "save snapshot when collecting an item" mode,
16359       // load last (current) snapshot for first "undo" after pressing "pause"
16360       // (else the last-but-one snapshot would be loaded, because the snapshot
16361       // pointer already points to the last snapshot when pressing "pause",
16362       // which is fine for "every step/move" mode, but not for "every collect")
16363       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16364           !game_undo_executed)
16365         steps--;
16366
16367       game_undo_executed = TRUE;
16368
16369       GameUndo(steps);
16370       break;
16371
16372     case GAME_CTRL_ID_REDO:
16373       GameRedo(steps);
16374       break;
16375
16376     case GAME_CTRL_ID_SAVE:
16377       TapeQuickSave();
16378       break;
16379
16380     case GAME_CTRL_ID_LOAD:
16381       TapeQuickLoad();
16382       break;
16383
16384     case SOUND_CTRL_ID_MUSIC:
16385     case SOUND_CTRL_ID_PANEL_MUSIC:
16386       if (setup.sound_music)
16387       { 
16388         setup.sound_music = FALSE;
16389
16390         FadeMusic();
16391       }
16392       else if (audio.music_available)
16393       { 
16394         setup.sound = setup.sound_music = TRUE;
16395
16396         SetAudioMode(setup.sound);
16397
16398         if (game_status == GAME_MODE_PLAYING)
16399           PlayLevelMusic();
16400       }
16401
16402       RedrawSoundButtonGadget(id);
16403
16404       break;
16405
16406     case SOUND_CTRL_ID_LOOPS:
16407     case SOUND_CTRL_ID_PANEL_LOOPS:
16408       if (setup.sound_loops)
16409         setup.sound_loops = FALSE;
16410       else if (audio.loops_available)
16411       {
16412         setup.sound = setup.sound_loops = TRUE;
16413
16414         SetAudioMode(setup.sound);
16415       }
16416
16417       RedrawSoundButtonGadget(id);
16418
16419       break;
16420
16421     case SOUND_CTRL_ID_SIMPLE:
16422     case SOUND_CTRL_ID_PANEL_SIMPLE:
16423       if (setup.sound_simple)
16424         setup.sound_simple = FALSE;
16425       else if (audio.sound_available)
16426       {
16427         setup.sound = setup.sound_simple = TRUE;
16428
16429         SetAudioMode(setup.sound);
16430       }
16431
16432       RedrawSoundButtonGadget(id);
16433
16434       break;
16435
16436     default:
16437       break;
16438   }
16439 }
16440
16441 static void HandleGameButtons(struct GadgetInfo *gi)
16442 {
16443   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16444 }
16445
16446 void HandleSoundButtonKeys(Key key)
16447 {
16448   if (key == setup.shortcut.sound_simple)
16449     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16450   else if (key == setup.shortcut.sound_loops)
16451     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16452   else if (key == setup.shortcut.sound_music)
16453     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16454 }