d37dee3a5f7c162ce69acdf33167a2d3b64f2233
[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 //                  http://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()             (local_player->LevelSolved_PanelOff)
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_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 /* game button identifiers */
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define SOUND_CTRL_ID_MUSIC             11
1020 #define SOUND_CTRL_ID_LOOPS             12
1021 #define SOUND_CTRL_ID_SIMPLE            13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC       14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS       15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE      16
1025
1026 #define NUM_GAME_BUTTONS                17
1027
1028
1029 /* forward declaration for internal use */
1030
1031 static void CreateField(int, int, int);
1032
1033 static void ResetGfxAnimation(int, int);
1034
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1037
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1042
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1047
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1054
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1058
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1062
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1067         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1072
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev)                             \
1075         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1077         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1079         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1080
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic();
1089 static void FadeLevelSoundsAndMusic();
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(void);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115
1116 static int getInvisibleActiveFromInvisibleElement(int);
1117 static int getInvisibleFromInvisibleActiveElement(int);
1118
1119 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1120
1121 /* for detection of endless loops, caused by custom element programming */
1122 /* (using maximal playfield width x 10 is just a rough approximation) */
1123 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1124
1125 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1126 {                                                                       \
1127   if (recursion_loop_detected)                                          \
1128     return (rc);                                                        \
1129                                                                         \
1130   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1131   {                                                                     \
1132     recursion_loop_detected = TRUE;                                     \
1133     recursion_loop_element = (e);                                       \
1134   }                                                                     \
1135                                                                         \
1136   recursion_loop_depth++;                                               \
1137 }
1138
1139 #define RECURSION_LOOP_DETECTION_END()                                  \
1140 {                                                                       \
1141   recursion_loop_depth--;                                               \
1142 }
1143
1144 static int recursion_loop_depth;
1145 static boolean recursion_loop_detected;
1146 static boolean recursion_loop_element;
1147
1148 static int map_player_action[MAX_PLAYERS];
1149
1150
1151 /* ------------------------------------------------------------------------- */
1152 /* definition of elements that automatically change to other elements after  */
1153 /* a specified time, eventually calling a function when changing             */
1154 /* ------------------------------------------------------------------------- */
1155
1156 /* forward declaration for changer functions */
1157 static void InitBuggyBase(int, int);
1158 static void WarnBuggyBase(int, int);
1159
1160 static void InitTrap(int, int);
1161 static void ActivateTrap(int, int);
1162 static void ChangeActiveTrap(int, int);
1163
1164 static void InitRobotWheel(int, int);
1165 static void RunRobotWheel(int, int);
1166 static void StopRobotWheel(int, int);
1167
1168 static void InitTimegateWheel(int, int);
1169 static void RunTimegateWheel(int, int);
1170
1171 static void InitMagicBallDelay(int, int);
1172 static void ActivateMagicBall(int, int);
1173
1174 struct ChangingElementInfo
1175 {
1176   int element;
1177   int target_element;
1178   int change_delay;
1179   void (*pre_change_function)(int x, int y);
1180   void (*change_function)(int x, int y);
1181   void (*post_change_function)(int x, int y);
1182 };
1183
1184 static struct ChangingElementInfo change_delay_list[] =
1185 {
1186   {
1187     EL_NUT_BREAKING,
1188     EL_EMERALD,
1189     6,
1190     NULL,
1191     NULL,
1192     NULL
1193   },
1194   {
1195     EL_PEARL_BREAKING,
1196     EL_EMPTY,
1197     8,
1198     NULL,
1199     NULL,
1200     NULL
1201   },
1202   {
1203     EL_EXIT_OPENING,
1204     EL_EXIT_OPEN,
1205     29,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_EXIT_CLOSING,
1212     EL_EXIT_CLOSED,
1213     29,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_STEEL_EXIT_OPENING,
1220     EL_STEEL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_STEEL_EXIT_CLOSING,
1228     EL_STEEL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_EM_EXIT_OPENING,
1236     EL_EM_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_EM_EXIT_CLOSING,
1244     EL_EMPTY,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_STEEL_EXIT_OPENING,
1252     EL_EM_STEEL_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_STEEL_EXIT_CLOSING,
1260     EL_STEELWALL,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_SP_EXIT_OPENING,
1268     EL_SP_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_SP_EXIT_CLOSING,
1276     EL_SP_EXIT_CLOSED,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SWITCHGATE_OPENING,
1284     EL_SWITCHGATE_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SWITCHGATE_CLOSING,
1292     EL_SWITCHGATE_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_TIMEGATE_OPENING,
1300     EL_TIMEGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_TIMEGATE_CLOSING,
1308     EL_TIMEGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314
1315   {
1316     EL_ACID_SPLASH_LEFT,
1317     EL_EMPTY,
1318     8,
1319     NULL,
1320     NULL,
1321     NULL
1322   },
1323   {
1324     EL_ACID_SPLASH_RIGHT,
1325     EL_EMPTY,
1326     8,
1327     NULL,
1328     NULL,
1329     NULL
1330   },
1331   {
1332     EL_SP_BUGGY_BASE,
1333     EL_SP_BUGGY_BASE_ACTIVATING,
1334     0,
1335     InitBuggyBase,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_SP_BUGGY_BASE_ACTIVATING,
1341     EL_SP_BUGGY_BASE_ACTIVE,
1342     0,
1343     InitBuggyBase,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE_ACTIVE,
1349     EL_SP_BUGGY_BASE,
1350     0,
1351     InitBuggyBase,
1352     WarnBuggyBase,
1353     NULL
1354   },
1355   {
1356     EL_TRAP,
1357     EL_TRAP_ACTIVE,
1358     0,
1359     InitTrap,
1360     NULL,
1361     ActivateTrap
1362   },
1363   {
1364     EL_TRAP_ACTIVE,
1365     EL_TRAP,
1366     31,
1367     NULL,
1368     ChangeActiveTrap,
1369     NULL
1370   },
1371   {
1372     EL_ROBOT_WHEEL_ACTIVE,
1373     EL_ROBOT_WHEEL,
1374     0,
1375     InitRobotWheel,
1376     RunRobotWheel,
1377     StopRobotWheel
1378   },
1379   {
1380     EL_TIMEGATE_SWITCH_ACTIVE,
1381     EL_TIMEGATE_SWITCH,
1382     0,
1383     InitTimegateWheel,
1384     RunTimegateWheel,
1385     NULL
1386   },
1387   {
1388     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1389     EL_DC_TIMEGATE_SWITCH,
1390     0,
1391     InitTimegateWheel,
1392     RunTimegateWheel,
1393     NULL
1394   },
1395   {
1396     EL_EMC_MAGIC_BALL_ACTIVE,
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     0,
1399     InitMagicBallDelay,
1400     NULL,
1401     ActivateMagicBall
1402   },
1403   {
1404     EL_EMC_SPRING_BUMPER_ACTIVE,
1405     EL_EMC_SPRING_BUMPER,
1406     8,
1407     NULL,
1408     NULL,
1409     NULL
1410   },
1411   {
1412     EL_DIAGONAL_SHRINKING,
1413     EL_UNDEFINED,
1414     0,
1415     NULL,
1416     NULL,
1417     NULL
1418   },
1419   {
1420     EL_DIAGONAL_GROWING,
1421     EL_UNDEFINED,
1422     0,
1423     NULL,
1424     NULL,
1425     NULL,
1426   },
1427
1428   {
1429     EL_UNDEFINED,
1430     EL_UNDEFINED,
1431     -1,
1432     NULL,
1433     NULL,
1434     NULL
1435   }
1436 };
1437
1438 struct
1439 {
1440   int element;
1441   int push_delay_fixed, push_delay_random;
1442 }
1443 push_delay_list[] =
1444 {
1445   { EL_SPRING,                  0, 0 },
1446   { EL_BALLOON,                 0, 0 },
1447
1448   { EL_SOKOBAN_OBJECT,          2, 0 },
1449   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1450   { EL_SATELLITE,               2, 0 },
1451   { EL_SP_DISK_YELLOW,          2, 0 },
1452
1453   { EL_UNDEFINED,               0, 0 },
1454 };
1455
1456 struct
1457 {
1458   int element;
1459   int move_stepsize;
1460 }
1461 move_stepsize_list[] =
1462 {
1463   { EL_AMOEBA_DROP,             2 },
1464   { EL_AMOEBA_DROPPING,         2 },
1465   { EL_QUICKSAND_FILLING,       1 },
1466   { EL_QUICKSAND_EMPTYING,      1 },
1467   { EL_QUICKSAND_FAST_FILLING,  2 },
1468   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1469   { EL_MAGIC_WALL_FILLING,      2 },
1470   { EL_MAGIC_WALL_EMPTYING,     2 },
1471   { EL_BD_MAGIC_WALL_FILLING,   2 },
1472   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1473   { EL_DC_MAGIC_WALL_FILLING,   2 },
1474   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1475
1476   { EL_UNDEFINED,               0 },
1477 };
1478
1479 struct
1480 {
1481   int element;
1482   int count;
1483 }
1484 collect_count_list[] =
1485 {
1486   { EL_EMERALD,                 1 },
1487   { EL_BD_DIAMOND,              1 },
1488   { EL_EMERALD_YELLOW,          1 },
1489   { EL_EMERALD_RED,             1 },
1490   { EL_EMERALD_PURPLE,          1 },
1491   { EL_DIAMOND,                 3 },
1492   { EL_SP_INFOTRON,             1 },
1493   { EL_PEARL,                   5 },
1494   { EL_CRYSTAL,                 8 },
1495
1496   { EL_UNDEFINED,               0 },
1497 };
1498
1499 struct
1500 {
1501   int element;
1502   int direction;
1503 }
1504 access_direction_list[] =
1505 {
1506   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1507   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1508   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1509   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1512   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1513   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1514   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1515   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1516   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1517
1518   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1519   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1520   { EL_SP_PORT_UP,                                                   MV_DOWN },
1521   { EL_SP_PORT_DOWN,                                         MV_UP           },
1522   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1523   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1524   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1525   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1526   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1527   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1529   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1530   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1531   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1532   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1533   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1534   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1535   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1536   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1537
1538   { EL_UNDEFINED,                       MV_NONE                              }
1539 };
1540
1541 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1542
1543 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1544 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1545 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1546                                  IS_JUST_CHANGING(x, y))
1547
1548 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1549
1550 /* static variables for playfield scan mode (scanning forward or backward) */
1551 static int playfield_scan_start_x = 0;
1552 static int playfield_scan_start_y = 0;
1553 static int playfield_scan_delta_x = 1;
1554 static int playfield_scan_delta_y = 1;
1555
1556 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1557                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1558                                      (y) += playfield_scan_delta_y)     \
1559                                 for ((x) = playfield_scan_start_x;      \
1560                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1561                                      (x) += playfield_scan_delta_x)
1562
1563 #ifdef DEBUG
1564 void DEBUG_SetMaximumDynamite()
1565 {
1566   int i;
1567
1568   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1569     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1570       local_player->inventory_element[local_player->inventory_size++] =
1571         EL_DYNAMITE;
1572 }
1573 #endif
1574
1575 static void InitPlayfieldScanModeVars()
1576 {
1577   if (game.use_reverse_scan_direction)
1578   {
1579     playfield_scan_start_x = lev_fieldx - 1;
1580     playfield_scan_start_y = lev_fieldy - 1;
1581
1582     playfield_scan_delta_x = -1;
1583     playfield_scan_delta_y = -1;
1584   }
1585   else
1586   {
1587     playfield_scan_start_x = 0;
1588     playfield_scan_start_y = 0;
1589
1590     playfield_scan_delta_x = 1;
1591     playfield_scan_delta_y = 1;
1592   }
1593 }
1594
1595 static void InitPlayfieldScanMode(int mode)
1596 {
1597   game.use_reverse_scan_direction =
1598     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1599
1600   InitPlayfieldScanModeVars();
1601 }
1602
1603 static int get_move_delay_from_stepsize(int move_stepsize)
1604 {
1605   move_stepsize =
1606     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1607
1608   /* make sure that stepsize value is always a power of 2 */
1609   move_stepsize = (1 << log_2(move_stepsize));
1610
1611   return TILEX / move_stepsize;
1612 }
1613
1614 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1615                                boolean init_game)
1616 {
1617   int player_nr = player->index_nr;
1618   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1619   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1620
1621   /* do no immediately change move delay -- the player might just be moving */
1622   player->move_delay_value_next = move_delay;
1623
1624   /* information if player can move must be set separately */
1625   player->cannot_move = cannot_move;
1626
1627   if (init_game)
1628   {
1629     player->move_delay       = game.initial_move_delay[player_nr];
1630     player->move_delay_value = game.initial_move_delay_value[player_nr];
1631
1632     player->move_delay_value_next = -1;
1633
1634     player->move_delay_reset_counter = 0;
1635   }
1636 }
1637
1638 void GetPlayerConfig()
1639 {
1640   GameFrameDelay = setup.game_frame_delay;
1641
1642   if (!audio.sound_available)
1643     setup.sound_simple = FALSE;
1644
1645   if (!audio.loops_available)
1646     setup.sound_loops = FALSE;
1647
1648   if (!audio.music_available)
1649     setup.sound_music = FALSE;
1650
1651   if (!video.fullscreen_available)
1652     setup.fullscreen = FALSE;
1653
1654   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1655
1656   SetAudioMode(setup.sound);
1657 }
1658
1659 int GetElementFromGroupElement(int element)
1660 {
1661   if (IS_GROUP_ELEMENT(element))
1662   {
1663     struct ElementGroupInfo *group = element_info[element].group;
1664     int last_anim_random_frame = gfx.anim_random_frame;
1665     int element_pos;
1666
1667     if (group->choice_mode == ANIM_RANDOM)
1668       gfx.anim_random_frame = RND(group->num_elements_resolved);
1669
1670     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1671                                     group->choice_mode, 0,
1672                                     group->choice_pos);
1673
1674     if (group->choice_mode == ANIM_RANDOM)
1675       gfx.anim_random_frame = last_anim_random_frame;
1676
1677     group->choice_pos++;
1678
1679     element = group->element_resolved[element_pos];
1680   }
1681
1682   return element;
1683 }
1684
1685 static void InitPlayerField(int x, int y, int element, boolean init_game)
1686 {
1687   if (element == EL_SP_MURPHY)
1688   {
1689     if (init_game)
1690     {
1691       if (stored_player[0].present)
1692       {
1693         Feld[x][y] = EL_SP_MURPHY_CLONE;
1694
1695         return;
1696       }
1697       else
1698       {
1699         stored_player[0].initial_element = element;
1700         stored_player[0].use_murphy = TRUE;
1701
1702         if (!level.use_artwork_element[0])
1703           stored_player[0].artwork_element = EL_SP_MURPHY;
1704       }
1705
1706       Feld[x][y] = EL_PLAYER_1;
1707     }
1708   }
1709
1710   if (init_game)
1711   {
1712     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1713     int jx = player->jx, jy = player->jy;
1714
1715     player->present = TRUE;
1716
1717     player->block_last_field = (element == EL_SP_MURPHY ?
1718                                 level.sp_block_last_field :
1719                                 level.block_last_field);
1720
1721     /* ---------- initialize player's last field block delay --------------- */
1722
1723     /* always start with reliable default value (no adjustment needed) */
1724     player->block_delay_adjustment = 0;
1725
1726     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1727     if (player->block_last_field && element == EL_SP_MURPHY)
1728       player->block_delay_adjustment = 1;
1729
1730     /* special case 2: in game engines before 3.1.1, blocking was different */
1731     if (game.use_block_last_field_bug)
1732       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1733
1734     if (!network.enabled || player->connected_network)
1735     {
1736       player->active = TRUE;
1737
1738       /* remove potentially duplicate players */
1739       if (StorePlayer[jx][jy] == Feld[x][y])
1740         StorePlayer[jx][jy] = 0;
1741
1742       StorePlayer[x][y] = Feld[x][y];
1743
1744 #if DEBUG_INIT_PLAYER
1745       if (options.debug)
1746       {
1747         printf("- player element %d activated", player->element_nr);
1748         printf(" (local player is %d and currently %s)\n",
1749                local_player->element_nr,
1750                local_player->active ? "active" : "not active");
1751       }
1752     }
1753 #endif
1754
1755     Feld[x][y] = EL_EMPTY;
1756
1757     player->jx = player->last_jx = x;
1758     player->jy = player->last_jy = y;
1759   }
1760
1761   if (!init_game)
1762   {
1763     int player_nr = GET_PLAYER_NR(element);
1764     struct PlayerInfo *player = &stored_player[player_nr];
1765
1766     if (player->active && player->killed)
1767       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1768   }
1769 }
1770
1771 static void InitField(int x, int y, boolean init_game)
1772 {
1773   int element = Feld[x][y];
1774
1775   switch (element)
1776   {
1777     case EL_SP_MURPHY:
1778     case EL_PLAYER_1:
1779     case EL_PLAYER_2:
1780     case EL_PLAYER_3:
1781     case EL_PLAYER_4:
1782       InitPlayerField(x, y, element, init_game);
1783       break;
1784
1785     case EL_SOKOBAN_FIELD_PLAYER:
1786       element = Feld[x][y] = EL_PLAYER_1;
1787       InitField(x, y, init_game);
1788
1789       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1790       InitField(x, y, init_game);
1791       break;
1792
1793     case EL_SOKOBAN_FIELD_EMPTY:
1794       local_player->sokobanfields_still_needed++;
1795       break;
1796
1797     case EL_STONEBLOCK:
1798       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1799         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1800       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1801         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1802       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1803         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1804       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1805         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1806       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1807         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1808       break;
1809
1810     case EL_BUG:
1811     case EL_BUG_RIGHT:
1812     case EL_BUG_UP:
1813     case EL_BUG_LEFT:
1814     case EL_BUG_DOWN:
1815     case EL_SPACESHIP:
1816     case EL_SPACESHIP_RIGHT:
1817     case EL_SPACESHIP_UP:
1818     case EL_SPACESHIP_LEFT:
1819     case EL_SPACESHIP_DOWN:
1820     case EL_BD_BUTTERFLY:
1821     case EL_BD_BUTTERFLY_RIGHT:
1822     case EL_BD_BUTTERFLY_UP:
1823     case EL_BD_BUTTERFLY_LEFT:
1824     case EL_BD_BUTTERFLY_DOWN:
1825     case EL_BD_FIREFLY:
1826     case EL_BD_FIREFLY_RIGHT:
1827     case EL_BD_FIREFLY_UP:
1828     case EL_BD_FIREFLY_LEFT:
1829     case EL_BD_FIREFLY_DOWN:
1830     case EL_PACMAN_RIGHT:
1831     case EL_PACMAN_UP:
1832     case EL_PACMAN_LEFT:
1833     case EL_PACMAN_DOWN:
1834     case EL_YAMYAM:
1835     case EL_YAMYAM_LEFT:
1836     case EL_YAMYAM_RIGHT:
1837     case EL_YAMYAM_UP:
1838     case EL_YAMYAM_DOWN:
1839     case EL_DARK_YAMYAM:
1840     case EL_ROBOT:
1841     case EL_PACMAN:
1842     case EL_SP_SNIKSNAK:
1843     case EL_SP_ELECTRON:
1844     case EL_MOLE:
1845     case EL_MOLE_LEFT:
1846     case EL_MOLE_RIGHT:
1847     case EL_MOLE_UP:
1848     case EL_MOLE_DOWN:
1849       InitMovDir(x, y);
1850       break;
1851
1852     case EL_AMOEBA_FULL:
1853     case EL_BD_AMOEBA:
1854       InitAmoebaNr(x, y);
1855       break;
1856
1857     case EL_AMOEBA_DROP:
1858       if (y == lev_fieldy - 1)
1859       {
1860         Feld[x][y] = EL_AMOEBA_GROWING;
1861         Store[x][y] = EL_AMOEBA_WET;
1862       }
1863       break;
1864
1865     case EL_DYNAMITE_ACTIVE:
1866     case EL_SP_DISK_RED_ACTIVE:
1867     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1868     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1869     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1870     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1871       MovDelay[x][y] = 96;
1872       break;
1873
1874     case EL_EM_DYNAMITE_ACTIVE:
1875       MovDelay[x][y] = 32;
1876       break;
1877
1878     case EL_LAMP:
1879       local_player->lights_still_needed++;
1880       break;
1881
1882     case EL_PENGUIN:
1883       local_player->friends_still_needed++;
1884       break;
1885
1886     case EL_PIG:
1887     case EL_DRAGON:
1888       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1889       break;
1890
1891     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1892     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1893     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1894     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1895     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1896     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1897     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1898     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1899     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1900     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1901     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1902     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1903       if (init_game)
1904       {
1905         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1906         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1907         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1908
1909         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1910         {
1911           game.belt_dir[belt_nr] = belt_dir;
1912           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1913         }
1914         else    /* more than one switch -- set it like the first switch */
1915         {
1916           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1917         }
1918       }
1919       break;
1920
1921     case EL_LIGHT_SWITCH_ACTIVE:
1922       if (init_game)
1923         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1924       break;
1925
1926     case EL_INVISIBLE_STEELWALL:
1927     case EL_INVISIBLE_WALL:
1928     case EL_INVISIBLE_SAND:
1929       if (game.light_time_left > 0 ||
1930           game.lenses_time_left > 0)
1931         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1932       break;
1933
1934     case EL_EMC_MAGIC_BALL:
1935       if (game.ball_state)
1936         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1937       break;
1938
1939     case EL_EMC_MAGIC_BALL_SWITCH:
1940       if (game.ball_state)
1941         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1942       break;
1943
1944     case EL_TRIGGER_PLAYER:
1945     case EL_TRIGGER_ELEMENT:
1946     case EL_TRIGGER_CE_VALUE:
1947     case EL_TRIGGER_CE_SCORE:
1948     case EL_SELF:
1949     case EL_ANY_ELEMENT:
1950     case EL_CURRENT_CE_VALUE:
1951     case EL_CURRENT_CE_SCORE:
1952     case EL_PREV_CE_1:
1953     case EL_PREV_CE_2:
1954     case EL_PREV_CE_3:
1955     case EL_PREV_CE_4:
1956     case EL_PREV_CE_5:
1957     case EL_PREV_CE_6:
1958     case EL_PREV_CE_7:
1959     case EL_PREV_CE_8:
1960     case EL_NEXT_CE_1:
1961     case EL_NEXT_CE_2:
1962     case EL_NEXT_CE_3:
1963     case EL_NEXT_CE_4:
1964     case EL_NEXT_CE_5:
1965     case EL_NEXT_CE_6:
1966     case EL_NEXT_CE_7:
1967     case EL_NEXT_CE_8:
1968       /* reference elements should not be used on the playfield */
1969       Feld[x][y] = EL_EMPTY;
1970       break;
1971
1972     default:
1973       if (IS_CUSTOM_ELEMENT(element))
1974       {
1975         if (CAN_MOVE(element))
1976           InitMovDir(x, y);
1977
1978         if (!element_info[element].use_last_ce_value || init_game)
1979           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1980       }
1981       else if (IS_GROUP_ELEMENT(element))
1982       {
1983         Feld[x][y] = GetElementFromGroupElement(element);
1984
1985         InitField(x, y, init_game);
1986       }
1987
1988       break;
1989   }
1990
1991   if (!init_game)
1992     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1993 }
1994
1995 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1996 {
1997   InitField(x, y, init_game);
1998
1999   /* not needed to call InitMovDir() -- already done by InitField()! */
2000   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2001       CAN_MOVE(Feld[x][y]))
2002     InitMovDir(x, y);
2003 }
2004
2005 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2006 {
2007   int old_element = Feld[x][y];
2008
2009   InitField(x, y, init_game);
2010
2011   /* not needed to call InitMovDir() -- already done by InitField()! */
2012   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2013       CAN_MOVE(old_element) &&
2014       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2015     InitMovDir(x, y);
2016
2017   /* this case is in fact a combination of not less than three bugs:
2018      first, it calls InitMovDir() for elements that can move, although this is
2019      already done by InitField(); then, it checks the element that was at this
2020      field _before_ the call to InitField() (which can change it); lastly, it
2021      was not called for "mole with direction" elements, which were treated as
2022      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2023   */
2024 }
2025
2026 static int get_key_element_from_nr(int key_nr)
2027 {
2028   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2029                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2030                           EL_EM_KEY_1 : EL_KEY_1);
2031
2032   return key_base_element + key_nr;
2033 }
2034
2035 static int get_next_dropped_element(struct PlayerInfo *player)
2036 {
2037   return (player->inventory_size > 0 ?
2038           player->inventory_element[player->inventory_size - 1] :
2039           player->inventory_infinite_element != EL_UNDEFINED ?
2040           player->inventory_infinite_element :
2041           player->dynabombs_left > 0 ?
2042           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2043           EL_UNDEFINED);
2044 }
2045
2046 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2047 {
2048   /* pos >= 0: get element from bottom of the stack;
2049      pos <  0: get element from top of the stack */
2050
2051   if (pos < 0)
2052   {
2053     int min_inventory_size = -pos;
2054     int inventory_pos = player->inventory_size - min_inventory_size;
2055     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2056
2057     return (player->inventory_size >= min_inventory_size ?
2058             player->inventory_element[inventory_pos] :
2059             player->inventory_infinite_element != EL_UNDEFINED ?
2060             player->inventory_infinite_element :
2061             player->dynabombs_left >= min_dynabombs_left ?
2062             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2063             EL_UNDEFINED);
2064   }
2065   else
2066   {
2067     int min_dynabombs_left = pos + 1;
2068     int min_inventory_size = pos + 1 - player->dynabombs_left;
2069     int inventory_pos = pos - player->dynabombs_left;
2070
2071     return (player->inventory_infinite_element != EL_UNDEFINED ?
2072             player->inventory_infinite_element :
2073             player->dynabombs_left >= min_dynabombs_left ?
2074             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2075             player->inventory_size >= min_inventory_size ?
2076             player->inventory_element[inventory_pos] :
2077             EL_UNDEFINED);
2078   }
2079 }
2080
2081 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2082 {
2083   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2084   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2085   int compare_result;
2086
2087   if (gpo1->sort_priority != gpo2->sort_priority)
2088     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2089   else
2090     compare_result = gpo1->nr - gpo2->nr;
2091
2092   return compare_result;
2093 }
2094
2095 int getPlayerInventorySize(int player_nr)
2096 {
2097   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2098     return level.native_em_level->ply[player_nr]->dynamite;
2099   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2100     return level.native_sp_level->game_sp->red_disk_count;
2101   else
2102     return stored_player[player_nr].inventory_size;
2103 }
2104
2105 void InitGameControlValues()
2106 {
2107   int i;
2108
2109   for (i = 0; game_panel_controls[i].nr != -1; i++)
2110   {
2111     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2112     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2113     struct TextPosInfo *pos = gpc->pos;
2114     int nr = gpc->nr;
2115     int type = gpc->type;
2116
2117     if (nr != i)
2118     {
2119       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2120       Error(ERR_EXIT, "this should not happen -- please debug");
2121     }
2122
2123     /* force update of game controls after initialization */
2124     gpc->value = gpc->last_value = -1;
2125     gpc->frame = gpc->last_frame = -1;
2126     gpc->gfx_frame = -1;
2127
2128     /* determine panel value width for later calculation of alignment */
2129     if (type == TYPE_INTEGER || type == TYPE_STRING)
2130     {
2131       pos->width = pos->size * getFontWidth(pos->font);
2132       pos->height = getFontHeight(pos->font);
2133     }
2134     else if (type == TYPE_ELEMENT)
2135     {
2136       pos->width = pos->size;
2137       pos->height = pos->size;
2138     }
2139
2140     /* fill structure for game panel draw order */
2141     gpo->nr = gpc->nr;
2142     gpo->sort_priority = pos->sort_priority;
2143   }
2144
2145   /* sort game panel controls according to sort_priority and control number */
2146   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2147         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2148 }
2149
2150 void UpdatePlayfieldElementCount()
2151 {
2152   boolean use_element_count = FALSE;
2153   int i, j, x, y;
2154
2155   /* first check if it is needed at all to calculate playfield element count */
2156   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2157     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2158       use_element_count = TRUE;
2159
2160   if (!use_element_count)
2161     return;
2162
2163   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2164     element_info[i].element_count = 0;
2165
2166   SCAN_PLAYFIELD(x, y)
2167   {
2168     element_info[Feld[x][y]].element_count++;
2169   }
2170
2171   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2172     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2173       if (IS_IN_GROUP(j, i))
2174         element_info[EL_GROUP_START + i].element_count +=
2175           element_info[j].element_count;
2176 }
2177
2178 void UpdateGameControlValues()
2179 {
2180   int i, k;
2181   int time = (local_player->LevelSolved ?
2182               local_player->LevelSolved_CountingTime :
2183               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2184               level.native_em_level->lev->time :
2185               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2186               level.native_sp_level->game_sp->time_played :
2187               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2188               game_mm.energy_left :
2189               game.no_time_limit ? TimePlayed : TimeLeft);
2190   int score = (local_player->LevelSolved ?
2191                local_player->LevelSolved_CountingScore :
2192                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2193                level.native_em_level->lev->score :
2194                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2195                level.native_sp_level->game_sp->score :
2196                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2197                game_mm.score :
2198                local_player->score);
2199   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2200               level.native_em_level->lev->required :
2201               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2202               level.native_sp_level->game_sp->infotrons_still_needed :
2203               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2204               game_mm.kettles_still_needed :
2205               local_player->gems_still_needed);
2206   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207                      level.native_em_level->lev->required > 0 :
2208                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2209                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2210                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2211                      game_mm.kettles_still_needed > 0 ||
2212                      game_mm.lights_still_needed > 0 :
2213                      local_player->gems_still_needed > 0 ||
2214                      local_player->sokobanfields_still_needed > 0 ||
2215                      local_player->lights_still_needed > 0);
2216   int health = (local_player->LevelSolved ?
2217                 local_player->LevelSolved_CountingHealth :
2218                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219                 MM_HEALTH(game_mm.laser_overload_value) :
2220                 local_player->health);
2221
2222   UpdatePlayfieldElementCount();
2223
2224   /* update game panel control values */
2225
2226   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2227   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2228
2229   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2230   for (i = 0; i < MAX_NUM_KEYS; i++)
2231     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2232   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2233   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2234
2235   if (game.centered_player_nr == -1)
2236   {
2237     for (i = 0; i < MAX_PLAYERS; i++)
2238     {
2239       /* only one player in Supaplex game engine */
2240       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2241         break;
2242
2243       for (k = 0; k < MAX_NUM_KEYS; k++)
2244       {
2245         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2246         {
2247           if (level.native_em_level->ply[i]->keys & (1 << k))
2248             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2249               get_key_element_from_nr(k);
2250         }
2251         else if (stored_player[i].key[k])
2252           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2253             get_key_element_from_nr(k);
2254       }
2255
2256       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2257         getPlayerInventorySize(i);
2258
2259       if (stored_player[i].num_white_keys > 0)
2260         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2261           EL_DC_KEY_WHITE;
2262
2263       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2264         stored_player[i].num_white_keys;
2265     }
2266   }
2267   else
2268   {
2269     int player_nr = game.centered_player_nr;
2270
2271     for (k = 0; k < MAX_NUM_KEYS; k++)
2272     {
2273       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2274       {
2275         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2276           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2277             get_key_element_from_nr(k);
2278       }
2279       else if (stored_player[player_nr].key[k])
2280         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2281           get_key_element_from_nr(k);
2282     }
2283
2284     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285       getPlayerInventorySize(player_nr);
2286
2287     if (stored_player[player_nr].num_white_keys > 0)
2288       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2289
2290     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2291       stored_player[player_nr].num_white_keys;
2292   }
2293
2294   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2295   {
2296     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2297       get_inventory_element_from_pos(local_player, i);
2298     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2299       get_inventory_element_from_pos(local_player, -i - 1);
2300   }
2301
2302   game_panel_controls[GAME_PANEL_SCORE].value = score;
2303   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2304
2305   game_panel_controls[GAME_PANEL_TIME].value = time;
2306
2307   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2308   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2309   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2310
2311   if (level.time == 0)
2312     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2313   else
2314     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2315
2316   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2317   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2318
2319   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2320
2321   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2322     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2323      EL_EMPTY);
2324   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2325     local_player->shield_normal_time_left;
2326   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2327     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2328      EL_EMPTY);
2329   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2330     local_player->shield_deadly_time_left;
2331
2332   game_panel_controls[GAME_PANEL_EXIT].value =
2333     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2334
2335   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2336     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2337   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2338     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2339      EL_EMC_MAGIC_BALL_SWITCH);
2340
2341   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2342     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2343   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2344     game.light_time_left;
2345
2346   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2347     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2348   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2349     game.timegate_time_left;
2350
2351   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2352     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2353
2354   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2355     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2356   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2357     game.lenses_time_left;
2358
2359   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2360     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2361   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2362     game.magnify_time_left;
2363
2364   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2365     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2366      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2367      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2368      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2369      EL_BALLOON_SWITCH_NONE);
2370
2371   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2372     local_player->dynabomb_count;
2373   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2374     local_player->dynabomb_size;
2375   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2376     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2377
2378   game_panel_controls[GAME_PANEL_PENGUINS].value =
2379     local_player->friends_still_needed;
2380
2381   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2382     local_player->sokobanfields_still_needed;
2383   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2384     local_player->sokobanfields_still_needed;
2385
2386   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2387     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2388
2389   for (i = 0; i < NUM_BELTS; i++)
2390   {
2391     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2392       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2393        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2394     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2395       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2396   }
2397
2398   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2399     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2400   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2401     game.magic_wall_time_left;
2402
2403   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2404     local_player->gravity;
2405
2406   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2407     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2408
2409   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2410     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2411       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2412        game.panel.element[i].id : EL_UNDEFINED);
2413
2414   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2415     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2416       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2417        element_info[game.panel.element_count[i].id].element_count : 0);
2418
2419   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2420     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2421       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2422        element_info[game.panel.ce_score[i].id].collect_score : 0);
2423
2424   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2425     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2426       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2427        element_info[game.panel.ce_score_element[i].id].collect_score :
2428        EL_UNDEFINED);
2429
2430   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2431   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2432   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2433
2434   /* update game panel control frames */
2435
2436   for (i = 0; game_panel_controls[i].nr != -1; i++)
2437   {
2438     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2439
2440     if (gpc->type == TYPE_ELEMENT)
2441     {
2442       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2443       {
2444         int last_anim_random_frame = gfx.anim_random_frame;
2445         int element = gpc->value;
2446         int graphic = el2panelimg(element);
2447
2448         if (gpc->value != gpc->last_value)
2449         {
2450           gpc->gfx_frame = 0;
2451           gpc->gfx_random = INIT_GFX_RANDOM();
2452         }
2453         else
2454         {
2455           gpc->gfx_frame++;
2456
2457           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2458               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2459             gpc->gfx_random = INIT_GFX_RANDOM();
2460         }
2461
2462         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2463           gfx.anim_random_frame = gpc->gfx_random;
2464
2465         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2466           gpc->gfx_frame = element_info[element].collect_score;
2467
2468         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2469                                               gpc->gfx_frame);
2470
2471         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2472           gfx.anim_random_frame = last_anim_random_frame;
2473       }
2474     }
2475     else if (gpc->type == TYPE_GRAPHIC)
2476     {
2477       if (gpc->graphic != IMG_UNDEFINED)
2478       {
2479         int last_anim_random_frame = gfx.anim_random_frame;
2480         int graphic = gpc->graphic;
2481
2482         if (gpc->value != gpc->last_value)
2483         {
2484           gpc->gfx_frame = 0;
2485           gpc->gfx_random = INIT_GFX_RANDOM();
2486         }
2487         else
2488         {
2489           gpc->gfx_frame++;
2490
2491           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2492               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2493             gpc->gfx_random = INIT_GFX_RANDOM();
2494         }
2495
2496         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2497           gfx.anim_random_frame = gpc->gfx_random;
2498
2499         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2500
2501         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2502           gfx.anim_random_frame = last_anim_random_frame;
2503       }
2504     }
2505   }
2506 }
2507
2508 void DisplayGameControlValues()
2509 {
2510   boolean redraw_panel = FALSE;
2511   int i;
2512
2513   for (i = 0; game_panel_controls[i].nr != -1; i++)
2514   {
2515     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2516
2517     if (PANEL_DEACTIVATED(gpc->pos))
2518       continue;
2519
2520     if (gpc->value == gpc->last_value &&
2521         gpc->frame == gpc->last_frame)
2522       continue;
2523
2524     redraw_panel = TRUE;
2525   }
2526
2527   if (!redraw_panel)
2528     return;
2529
2530   /* copy default game door content to main double buffer */
2531
2532   /* !!! CHECK AGAIN !!! */
2533   SetPanelBackground();
2534   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2535   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2536
2537   /* redraw game control buttons */
2538   RedrawGameButtons();
2539
2540   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2541
2542   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2543   {
2544     int nr = game_panel_order[i].nr;
2545     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2546     struct TextPosInfo *pos = gpc->pos;
2547     int type = gpc->type;
2548     int value = gpc->value;
2549     int frame = gpc->frame;
2550     int size = pos->size;
2551     int font = pos->font;
2552     boolean draw_masked = pos->draw_masked;
2553     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2554
2555     if (PANEL_DEACTIVATED(pos))
2556       continue;
2557
2558     gpc->last_value = value;
2559     gpc->last_frame = frame;
2560
2561     if (type == TYPE_INTEGER)
2562     {
2563       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2564           nr == GAME_PANEL_TIME)
2565       {
2566         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2567
2568         if (use_dynamic_size)           /* use dynamic number of digits */
2569         {
2570           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2571           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2572           int size2 = size1 + 1;
2573           int font1 = pos->font;
2574           int font2 = pos->font_alt;
2575
2576           size = (value < value_change ? size1 : size2);
2577           font = (value < value_change ? font1 : font2);
2578         }
2579       }
2580
2581       /* correct text size if "digits" is zero or less */
2582       if (size <= 0)
2583         size = strlen(int2str(value, size));
2584
2585       /* dynamically correct text alignment */
2586       pos->width = size * getFontWidth(font);
2587
2588       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2589                   int2str(value, size), font, mask_mode);
2590     }
2591     else if (type == TYPE_ELEMENT)
2592     {
2593       int element, graphic;
2594       Bitmap *src_bitmap;
2595       int src_x, src_y;
2596       int width, height;
2597       int dst_x = PANEL_XPOS(pos);
2598       int dst_y = PANEL_YPOS(pos);
2599
2600       if (value != EL_UNDEFINED && value != EL_EMPTY)
2601       {
2602         element = value;
2603         graphic = el2panelimg(value);
2604
2605         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2606
2607         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2608           size = TILESIZE;
2609
2610         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2611                               &src_x, &src_y);
2612
2613         width  = graphic_info[graphic].width  * size / TILESIZE;
2614         height = graphic_info[graphic].height * size / TILESIZE;
2615
2616         if (draw_masked)
2617           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2618                            dst_x, dst_y);
2619         else
2620           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2621                      dst_x, dst_y);
2622       }
2623     }
2624     else if (type == TYPE_GRAPHIC)
2625     {
2626       int graphic        = gpc->graphic;
2627       int graphic_active = gpc->graphic_active;
2628       Bitmap *src_bitmap;
2629       int src_x, src_y;
2630       int width, height;
2631       int dst_x = PANEL_XPOS(pos);
2632       int dst_y = PANEL_YPOS(pos);
2633       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2634                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2635
2636       if (graphic != IMG_UNDEFINED && !skip)
2637       {
2638         if (pos->style == STYLE_REVERSE)
2639           value = 100 - value;
2640
2641         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2642
2643         if (pos->direction & MV_HORIZONTAL)
2644         {
2645           width  = graphic_info[graphic_active].width * value / 100;
2646           height = graphic_info[graphic_active].height;
2647
2648           if (pos->direction == MV_LEFT)
2649           {
2650             src_x += graphic_info[graphic_active].width - width;
2651             dst_x += graphic_info[graphic_active].width - width;
2652           }
2653         }
2654         else
2655         {
2656           width  = graphic_info[graphic_active].width;
2657           height = graphic_info[graphic_active].height * value / 100;
2658
2659           if (pos->direction == MV_UP)
2660           {
2661             src_y += graphic_info[graphic_active].height - height;
2662             dst_y += graphic_info[graphic_active].height - height;
2663           }
2664         }
2665
2666         if (draw_masked)
2667           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2668                            dst_x, dst_y);
2669         else
2670           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2671                      dst_x, dst_y);
2672
2673         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2674
2675         if (pos->direction & MV_HORIZONTAL)
2676         {
2677           if (pos->direction == MV_RIGHT)
2678           {
2679             src_x += width;
2680             dst_x += width;
2681           }
2682           else
2683           {
2684             dst_x = PANEL_XPOS(pos);
2685           }
2686
2687           width = graphic_info[graphic].width - width;
2688         }
2689         else
2690         {
2691           if (pos->direction == MV_DOWN)
2692           {
2693             src_y += height;
2694             dst_y += height;
2695           }
2696           else
2697           {
2698             dst_y = PANEL_YPOS(pos);
2699           }
2700
2701           height = graphic_info[graphic].height - height;
2702         }
2703
2704         if (draw_masked)
2705           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2706                            dst_x, dst_y);
2707         else
2708           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2709                      dst_x, dst_y);
2710       }
2711     }
2712     else if (type == TYPE_STRING)
2713     {
2714       boolean active = (value != 0);
2715       char *state_normal = "off";
2716       char *state_active = "on";
2717       char *state = (active ? state_active : state_normal);
2718       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2719                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2720                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2721                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2722
2723       if (nr == GAME_PANEL_GRAVITY_STATE)
2724       {
2725         int font1 = pos->font;          /* (used for normal state) */
2726         int font2 = pos->font_alt;      /* (used for active state) */
2727
2728         font = (active ? font2 : font1);
2729       }
2730
2731       if (s != NULL)
2732       {
2733         char *s_cut;
2734
2735         if (size <= 0)
2736         {
2737           /* don't truncate output if "chars" is zero or less */
2738           size = strlen(s);
2739
2740           /* dynamically correct text alignment */
2741           pos->width = size * getFontWidth(font);
2742         }
2743
2744         s_cut = getStringCopyN(s, size);
2745
2746         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2747                     s_cut, font, mask_mode);
2748
2749         free(s_cut);
2750       }
2751     }
2752
2753     redraw_mask |= REDRAW_DOOR_1;
2754   }
2755
2756   SetGameStatus(GAME_MODE_PLAYING);
2757 }
2758
2759 void UpdateAndDisplayGameControlValues()
2760 {
2761   if (tape.deactivate_display)
2762     return;
2763
2764   UpdateGameControlValues();
2765   DisplayGameControlValues();
2766 }
2767
2768 void UpdateGameDoorValues()
2769 {
2770   UpdateGameControlValues();
2771 }
2772
2773 void DrawGameDoorValues()
2774 {
2775   DisplayGameControlValues();
2776 }
2777
2778
2779 /*
2780   =============================================================================
2781   InitGameEngine()
2782   -----------------------------------------------------------------------------
2783   initialize game engine due to level / tape version number
2784   =============================================================================
2785 */
2786
2787 static void InitGameEngine()
2788 {
2789   int i, j, k, l, x, y;
2790
2791   /* set game engine from tape file when re-playing, else from level file */
2792   game.engine_version = (tape.playing ? tape.engine_version :
2793                          level.game_version);
2794
2795   /* set single or multi-player game mode (needed for re-playing tapes) */
2796   game.team_mode = setup.team_mode;
2797
2798   if (tape.playing)
2799   {
2800     int num_players = 0;
2801
2802     for (i = 0; i < MAX_PLAYERS; i++)
2803       if (tape.player_participates[i])
2804         num_players++;
2805
2806     /* multi-player tapes contain input data for more than one player */
2807     game.team_mode = (num_players > 1);
2808   }
2809
2810   /* ---------------------------------------------------------------------- */
2811   /* set flags for bugs and changes according to active game engine version */
2812   /* ---------------------------------------------------------------------- */
2813
2814   /*
2815     Summary of bugfix/change:
2816     Fixed handling for custom elements that change when pushed by the player.
2817
2818     Fixed/changed in version:
2819     3.1.0
2820
2821     Description:
2822     Before 3.1.0, custom elements that "change when pushing" changed directly
2823     after the player started pushing them (until then handled in "DigField()").
2824     Since 3.1.0, these custom elements are not changed until the "pushing"
2825     move of the element is finished (now handled in "ContinueMoving()").
2826
2827     Affected levels/tapes:
2828     The first condition is generally needed for all levels/tapes before version
2829     3.1.0, which might use the old behaviour before it was changed; known tapes
2830     that are affected are some tapes from the level set "Walpurgis Gardens" by
2831     Jamie Cullen.
2832     The second condition is an exception from the above case and is needed for
2833     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2834     above (including some development versions of 3.1.0), but before it was
2835     known that this change would break tapes like the above and was fixed in
2836     3.1.1, so that the changed behaviour was active although the engine version
2837     while recording maybe was before 3.1.0. There is at least one tape that is
2838     affected by this exception, which is the tape for the one-level set "Bug
2839     Machine" by Juergen Bonhagen.
2840   */
2841
2842   game.use_change_when_pushing_bug =
2843     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2844      !(tape.playing &&
2845        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2846        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2847
2848   /*
2849     Summary of bugfix/change:
2850     Fixed handling for blocking the field the player leaves when moving.
2851
2852     Fixed/changed in version:
2853     3.1.1
2854
2855     Description:
2856     Before 3.1.1, when "block last field when moving" was enabled, the field
2857     the player is leaving when moving was blocked for the time of the move,
2858     and was directly unblocked afterwards. This resulted in the last field
2859     being blocked for exactly one less than the number of frames of one player
2860     move. Additionally, even when blocking was disabled, the last field was
2861     blocked for exactly one frame.
2862     Since 3.1.1, due to changes in player movement handling, the last field
2863     is not blocked at all when blocking is disabled. When blocking is enabled,
2864     the last field is blocked for exactly the number of frames of one player
2865     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2866     last field is blocked for exactly one more than the number of frames of
2867     one player move.
2868
2869     Affected levels/tapes:
2870     (!!! yet to be determined -- probably many !!!)
2871   */
2872
2873   game.use_block_last_field_bug =
2874     (game.engine_version < VERSION_IDENT(3,1,1,0));
2875
2876   game_em.use_single_button =
2877     (game.engine_version > VERSION_IDENT(4,0,0,2));
2878
2879   game_em.use_snap_key_bug =
2880     (game.engine_version < VERSION_IDENT(4,0,1,0));
2881
2882   /* ---------------------------------------------------------------------- */
2883
2884   /* set maximal allowed number of custom element changes per game frame */
2885   game.max_num_changes_per_frame = 1;
2886
2887   /* default scan direction: scan playfield from top/left to bottom/right */
2888   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2889
2890   /* dynamically adjust element properties according to game engine version */
2891   InitElementPropertiesEngine(game.engine_version);
2892
2893 #if 0
2894   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2895   printf("          tape version == %06d [%s] [file: %06d]\n",
2896          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2897          tape.file_version);
2898   printf("       => game.engine_version == %06d\n", game.engine_version);
2899 #endif
2900
2901   /* ---------- initialize player's initial move delay --------------------- */
2902
2903   /* dynamically adjust player properties according to level information */
2904   for (i = 0; i < MAX_PLAYERS; i++)
2905     game.initial_move_delay_value[i] =
2906       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2907
2908   /* dynamically adjust player properties according to game engine version */
2909   for (i = 0; i < MAX_PLAYERS; i++)
2910     game.initial_move_delay[i] =
2911       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2912        game.initial_move_delay_value[i] : 0);
2913
2914   /* ---------- initialize player's initial push delay --------------------- */
2915
2916   /* dynamically adjust player properties according to game engine version */
2917   game.initial_push_delay_value =
2918     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2919
2920   /* ---------- initialize changing elements ------------------------------- */
2921
2922   /* initialize changing elements information */
2923   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2924   {
2925     struct ElementInfo *ei = &element_info[i];
2926
2927     /* this pointer might have been changed in the level editor */
2928     ei->change = &ei->change_page[0];
2929
2930     if (!IS_CUSTOM_ELEMENT(i))
2931     {
2932       ei->change->target_element = EL_EMPTY_SPACE;
2933       ei->change->delay_fixed = 0;
2934       ei->change->delay_random = 0;
2935       ei->change->delay_frames = 1;
2936     }
2937
2938     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2939     {
2940       ei->has_change_event[j] = FALSE;
2941
2942       ei->event_page_nr[j] = 0;
2943       ei->event_page[j] = &ei->change_page[0];
2944     }
2945   }
2946
2947   /* add changing elements from pre-defined list */
2948   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2949   {
2950     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2951     struct ElementInfo *ei = &element_info[ch_delay->element];
2952
2953     ei->change->target_element       = ch_delay->target_element;
2954     ei->change->delay_fixed          = ch_delay->change_delay;
2955
2956     ei->change->pre_change_function  = ch_delay->pre_change_function;
2957     ei->change->change_function      = ch_delay->change_function;
2958     ei->change->post_change_function = ch_delay->post_change_function;
2959
2960     ei->change->can_change = TRUE;
2961     ei->change->can_change_or_has_action = TRUE;
2962
2963     ei->has_change_event[CE_DELAY] = TRUE;
2964
2965     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2966     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2967   }
2968
2969   /* ---------- initialize internal run-time variables --------------------- */
2970
2971   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2972   {
2973     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2974
2975     for (j = 0; j < ei->num_change_pages; j++)
2976     {
2977       ei->change_page[j].can_change_or_has_action =
2978         (ei->change_page[j].can_change |
2979          ei->change_page[j].has_action);
2980     }
2981   }
2982
2983   /* add change events from custom element configuration */
2984   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2985   {
2986     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2987
2988     for (j = 0; j < ei->num_change_pages; j++)
2989     {
2990       if (!ei->change_page[j].can_change_or_has_action)
2991         continue;
2992
2993       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2994       {
2995         /* only add event page for the first page found with this event */
2996         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2997         {
2998           ei->has_change_event[k] = TRUE;
2999
3000           ei->event_page_nr[k] = j;
3001           ei->event_page[k] = &ei->change_page[j];
3002         }
3003       }
3004     }
3005   }
3006
3007   /* ---------- initialize reference elements in change conditions --------- */
3008
3009   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3010   {
3011     int element = EL_CUSTOM_START + i;
3012     struct ElementInfo *ei = &element_info[element];
3013
3014     for (j = 0; j < ei->num_change_pages; j++)
3015     {
3016       int trigger_element = ei->change_page[j].initial_trigger_element;
3017
3018       if (trigger_element >= EL_PREV_CE_8 &&
3019           trigger_element <= EL_NEXT_CE_8)
3020         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3021
3022       ei->change_page[j].trigger_element = trigger_element;
3023     }
3024   }
3025
3026   /* ---------- initialize run-time trigger player and element ------------- */
3027
3028   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3029   {
3030     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3031
3032     for (j = 0; j < ei->num_change_pages; j++)
3033     {
3034       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3035       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3036       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3037       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3038       ei->change_page[j].actual_trigger_ce_value = 0;
3039       ei->change_page[j].actual_trigger_ce_score = 0;
3040     }
3041   }
3042
3043   /* ---------- initialize trigger events ---------------------------------- */
3044
3045   /* initialize trigger events information */
3046   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3047     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3048       trigger_events[i][j] = FALSE;
3049
3050   /* add trigger events from element change event properties */
3051   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3052   {
3053     struct ElementInfo *ei = &element_info[i];
3054
3055     for (j = 0; j < ei->num_change_pages; j++)
3056     {
3057       if (!ei->change_page[j].can_change_or_has_action)
3058         continue;
3059
3060       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3061       {
3062         int trigger_element = ei->change_page[j].trigger_element;
3063
3064         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3065         {
3066           if (ei->change_page[j].has_event[k])
3067           {
3068             if (IS_GROUP_ELEMENT(trigger_element))
3069             {
3070               struct ElementGroupInfo *group =
3071                 element_info[trigger_element].group;
3072
3073               for (l = 0; l < group->num_elements_resolved; l++)
3074                 trigger_events[group->element_resolved[l]][k] = TRUE;
3075             }
3076             else if (trigger_element == EL_ANY_ELEMENT)
3077               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3078                 trigger_events[l][k] = TRUE;
3079             else
3080               trigger_events[trigger_element][k] = TRUE;
3081           }
3082         }
3083       }
3084     }
3085   }
3086
3087   /* ---------- initialize push delay -------------------------------------- */
3088
3089   /* initialize push delay values to default */
3090   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3091   {
3092     if (!IS_CUSTOM_ELEMENT(i))
3093     {
3094       /* set default push delay values (corrected since version 3.0.7-1) */
3095       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3096       {
3097         element_info[i].push_delay_fixed = 2;
3098         element_info[i].push_delay_random = 8;
3099       }
3100       else
3101       {
3102         element_info[i].push_delay_fixed = 8;
3103         element_info[i].push_delay_random = 8;
3104       }
3105     }
3106   }
3107
3108   /* set push delay value for certain elements from pre-defined list */
3109   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3110   {
3111     int e = push_delay_list[i].element;
3112
3113     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3114     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3115   }
3116
3117   /* set push delay value for Supaplex elements for newer engine versions */
3118   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3119   {
3120     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3121     {
3122       if (IS_SP_ELEMENT(i))
3123       {
3124         /* set SP push delay to just enough to push under a falling zonk */
3125         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3126
3127         element_info[i].push_delay_fixed  = delay;
3128         element_info[i].push_delay_random = 0;
3129       }
3130     }
3131   }
3132
3133   /* ---------- initialize move stepsize ----------------------------------- */
3134
3135   /* initialize move stepsize values to default */
3136   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3137     if (!IS_CUSTOM_ELEMENT(i))
3138       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3139
3140   /* set move stepsize value for certain elements from pre-defined list */
3141   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3142   {
3143     int e = move_stepsize_list[i].element;
3144
3145     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3146   }
3147
3148   /* ---------- initialize collect score ----------------------------------- */
3149
3150   /* initialize collect score values for custom elements from initial value */
3151   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152     if (IS_CUSTOM_ELEMENT(i))
3153       element_info[i].collect_score = element_info[i].collect_score_initial;
3154
3155   /* ---------- initialize collect count ----------------------------------- */
3156
3157   /* initialize collect count values for non-custom elements */
3158   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3159     if (!IS_CUSTOM_ELEMENT(i))
3160       element_info[i].collect_count_initial = 0;
3161
3162   /* add collect count values for all elements from pre-defined list */
3163   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3164     element_info[collect_count_list[i].element].collect_count_initial =
3165       collect_count_list[i].count;
3166
3167   /* ---------- initialize access direction -------------------------------- */
3168
3169   /* initialize access direction values to default (access from every side) */
3170   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3171     if (!IS_CUSTOM_ELEMENT(i))
3172       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3173
3174   /* set access direction value for certain elements from pre-defined list */
3175   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3176     element_info[access_direction_list[i].element].access_direction =
3177       access_direction_list[i].direction;
3178
3179   /* ---------- initialize explosion content ------------------------------- */
3180   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3181   {
3182     if (IS_CUSTOM_ELEMENT(i))
3183       continue;
3184
3185     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3186     {
3187       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3188
3189       element_info[i].content.e[x][y] =
3190         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3191          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3192          i == EL_PLAYER_3 ? EL_EMERALD :
3193          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3194          i == EL_MOLE ? EL_EMERALD_RED :
3195          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3196          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3197          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3198          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3199          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3200          i == EL_WALL_EMERALD ? EL_EMERALD :
3201          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3202          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3203          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3204          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3205          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3206          i == EL_WALL_PEARL ? EL_PEARL :
3207          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3208          EL_EMPTY);
3209     }
3210   }
3211
3212   /* ---------- initialize recursion detection ------------------------------ */
3213   recursion_loop_depth = 0;
3214   recursion_loop_detected = FALSE;
3215   recursion_loop_element = EL_UNDEFINED;
3216
3217   /* ---------- initialize graphics engine ---------------------------------- */
3218   game.scroll_delay_value =
3219     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3220      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3221   game.scroll_delay_value =
3222     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3223
3224   /* ---------- initialize game engine snapshots ---------------------------- */
3225   for (i = 0; i < MAX_PLAYERS; i++)
3226     game.snapshot.last_action[i] = 0;
3227   game.snapshot.changed_action = FALSE;
3228   game.snapshot.collected_item = FALSE;
3229   game.snapshot.mode =
3230     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3231      SNAPSHOT_MODE_EVERY_STEP :
3232      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3233      SNAPSHOT_MODE_EVERY_MOVE :
3234      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3235      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3236   game.snapshot.save_snapshot = FALSE;
3237
3238   /* ---------- initialize level time for Supaplex engine ------------------- */
3239   /* Supaplex levels with time limit currently unsupported -- should be added */
3240   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3241     level.time = 0;
3242 }
3243
3244 int get_num_special_action(int element, int action_first, int action_last)
3245 {
3246   int num_special_action = 0;
3247   int i, j;
3248
3249   for (i = action_first; i <= action_last; i++)
3250   {
3251     boolean found = FALSE;
3252
3253     for (j = 0; j < NUM_DIRECTIONS; j++)
3254       if (el_act_dir2img(element, i, j) !=
3255           el_act_dir2img(element, ACTION_DEFAULT, j))
3256         found = TRUE;
3257
3258     if (found)
3259       num_special_action++;
3260     else
3261       break;
3262   }
3263
3264   return num_special_action;
3265 }
3266
3267
3268 /*
3269   =============================================================================
3270   InitGame()
3271   -----------------------------------------------------------------------------
3272   initialize and start new game
3273   =============================================================================
3274 */
3275
3276 void InitGame()
3277 {
3278   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3279   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3280   int fade_mask = REDRAW_FIELD;
3281
3282   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3283   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3284   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3285   int initial_move_dir = MV_DOWN;
3286   int i, j, x, y;
3287
3288   // required here to update video display before fading (FIX THIS)
3289   DrawMaskedBorder(REDRAW_DOOR_2);
3290
3291   if (!game.restart_level)
3292     CloseDoor(DOOR_CLOSE_1);
3293
3294   SetGameStatus(GAME_MODE_PLAYING);
3295
3296   if (level_editor_test_game)
3297     FadeSkipNextFadeIn();
3298   else
3299     FadeSetEnterScreen();
3300
3301   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3302     fade_mask = REDRAW_ALL;
3303
3304   FadeLevelSoundsAndMusic();
3305
3306   ExpireSoundLoops(TRUE);
3307
3308   if (!level_editor_test_game)
3309     FadeOut(fade_mask);
3310
3311   /* needed if different viewport properties defined for playing */
3312   ChangeViewportPropertiesIfNeeded();
3313
3314   ClearField();
3315
3316   DrawCompleteVideoDisplay();
3317
3318   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3319
3320   InitGameEngine();
3321   InitGameControlValues();
3322
3323   /* don't play tapes over network */
3324   network_playing = (network.enabled && !tape.playing);
3325
3326   for (i = 0; i < MAX_PLAYERS; i++)
3327   {
3328     struct PlayerInfo *player = &stored_player[i];
3329
3330     player->index_nr = i;
3331     player->index_bit = (1 << i);
3332     player->element_nr = EL_PLAYER_1 + i;
3333
3334     player->present = FALSE;
3335     player->active = FALSE;
3336     player->mapped = FALSE;
3337
3338     player->killed = FALSE;
3339     player->reanimated = FALSE;
3340
3341     player->action = 0;
3342     player->effective_action = 0;
3343     player->programmed_action = 0;
3344
3345     player->mouse_action.lx = 0;
3346     player->mouse_action.ly = 0;
3347     player->mouse_action.button = 0;
3348     player->mouse_action.button_hint = 0;
3349
3350     player->effective_mouse_action.lx = 0;
3351     player->effective_mouse_action.ly = 0;
3352     player->effective_mouse_action.button = 0;
3353     player->effective_mouse_action.button_hint = 0;
3354
3355     player->score = 0;
3356     player->score_final = 0;
3357
3358     player->health = MAX_HEALTH;
3359     player->health_final = MAX_HEALTH;
3360
3361     player->gems_still_needed = level.gems_needed;
3362     player->sokobanfields_still_needed = 0;
3363     player->lights_still_needed = 0;
3364     player->friends_still_needed = 0;
3365
3366     for (j = 0; j < MAX_NUM_KEYS; j++)
3367       player->key[j] = FALSE;
3368
3369     player->num_white_keys = 0;
3370
3371     player->dynabomb_count = 0;
3372     player->dynabomb_size = 1;
3373     player->dynabombs_left = 0;
3374     player->dynabomb_xl = FALSE;
3375
3376     player->MovDir = initial_move_dir;
3377     player->MovPos = 0;
3378     player->GfxPos = 0;
3379     player->GfxDir = initial_move_dir;
3380     player->GfxAction = ACTION_DEFAULT;
3381     player->Frame = 0;
3382     player->StepFrame = 0;
3383
3384     player->initial_element = player->element_nr;
3385     player->artwork_element =
3386       (level.use_artwork_element[i] ? level.artwork_element[i] :
3387        player->element_nr);
3388     player->use_murphy = FALSE;
3389
3390     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3391     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3392
3393     player->gravity = level.initial_player_gravity[i];
3394
3395     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3396
3397     player->actual_frame_counter = 0;
3398
3399     player->step_counter = 0;
3400
3401     player->last_move_dir = initial_move_dir;
3402
3403     player->is_active = FALSE;
3404
3405     player->is_waiting = FALSE;
3406     player->is_moving = FALSE;
3407     player->is_auto_moving = FALSE;
3408     player->is_digging = FALSE;
3409     player->is_snapping = FALSE;
3410     player->is_collecting = FALSE;
3411     player->is_pushing = FALSE;
3412     player->is_switching = FALSE;
3413     player->is_dropping = FALSE;
3414     player->is_dropping_pressed = FALSE;
3415
3416     player->is_bored = FALSE;
3417     player->is_sleeping = FALSE;
3418
3419     player->was_waiting = TRUE;
3420     player->was_moving = FALSE;
3421     player->was_snapping = FALSE;
3422     player->was_dropping = FALSE;
3423
3424     player->force_dropping = FALSE;
3425
3426     player->frame_counter_bored = -1;
3427     player->frame_counter_sleeping = -1;
3428
3429     player->anim_delay_counter = 0;
3430     player->post_delay_counter = 0;
3431
3432     player->dir_waiting = initial_move_dir;
3433     player->action_waiting = ACTION_DEFAULT;
3434     player->last_action_waiting = ACTION_DEFAULT;
3435     player->special_action_bored = ACTION_DEFAULT;
3436     player->special_action_sleeping = ACTION_DEFAULT;
3437
3438     player->switch_x = -1;
3439     player->switch_y = -1;
3440
3441     player->drop_x = -1;
3442     player->drop_y = -1;
3443
3444     player->show_envelope = 0;
3445
3446     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3447
3448     player->push_delay       = -1;      /* initialized when pushing starts */
3449     player->push_delay_value = game.initial_push_delay_value;
3450
3451     player->drop_delay = 0;
3452     player->drop_pressed_delay = 0;
3453
3454     player->last_jx = -1;
3455     player->last_jy = -1;
3456     player->jx = -1;
3457     player->jy = -1;
3458
3459     player->shield_normal_time_left = 0;
3460     player->shield_deadly_time_left = 0;
3461
3462     player->inventory_infinite_element = EL_UNDEFINED;
3463     player->inventory_size = 0;
3464
3465     if (level.use_initial_inventory[i])
3466     {
3467       for (j = 0; j < level.initial_inventory_size[i]; j++)
3468       {
3469         int element = level.initial_inventory_content[i][j];
3470         int collect_count = element_info[element].collect_count_initial;
3471         int k;
3472
3473         if (!IS_CUSTOM_ELEMENT(element))
3474           collect_count = 1;
3475
3476         if (collect_count == 0)
3477           player->inventory_infinite_element = element;
3478         else
3479           for (k = 0; k < collect_count; k++)
3480             if (player->inventory_size < MAX_INVENTORY_SIZE)
3481               player->inventory_element[player->inventory_size++] = element;
3482       }
3483     }
3484
3485     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3486     SnapField(player, 0, 0);
3487
3488     player->LevelSolved = FALSE;
3489     player->GameOver = FALSE;
3490
3491     player->LevelSolved_GameWon = FALSE;
3492     player->LevelSolved_GameEnd = FALSE;
3493     player->LevelSolved_PanelOff = FALSE;
3494     player->LevelSolved_SaveTape = FALSE;
3495     player->LevelSolved_SaveScore = FALSE;
3496
3497     player->LevelSolved_CountingTime = 0;
3498     player->LevelSolved_CountingScore = 0;
3499     player->LevelSolved_CountingHealth = 0;
3500
3501     map_player_action[i] = i;
3502   }
3503
3504   network_player_action_received = FALSE;
3505
3506   /* initial null action */
3507   if (network_playing)
3508     SendToServer_MovePlayer(MV_NONE);
3509
3510   ZX = ZY = -1;
3511   ExitX = ExitY = -1;
3512
3513   FrameCounter = 0;
3514   TimeFrames = 0;
3515   TimePlayed = 0;
3516   TimeLeft = level.time;
3517   TapeTime = 0;
3518
3519   ScreenMovDir = MV_NONE;
3520   ScreenMovPos = 0;
3521   ScreenGfxPos = 0;
3522
3523   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3524
3525   AllPlayersGone = FALSE;
3526
3527   game.no_time_limit = (level.time == 0);
3528
3529   game.yamyam_content_nr = 0;
3530   game.robot_wheel_active = FALSE;
3531   game.magic_wall_active = FALSE;
3532   game.magic_wall_time_left = 0;
3533   game.light_time_left = 0;
3534   game.timegate_time_left = 0;
3535   game.switchgate_pos = 0;
3536   game.wind_direction = level.wind_direction_initial;
3537
3538   game.lenses_time_left = 0;
3539   game.magnify_time_left = 0;
3540
3541   game.ball_state = level.ball_state_initial;
3542   game.ball_content_nr = 0;
3543
3544   game.envelope_active = FALSE;
3545
3546   for (i = 0; i < NUM_BELTS; i++)
3547   {
3548     game.belt_dir[i] = MV_NONE;
3549     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3550   }
3551
3552   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3553     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3554
3555 #if DEBUG_INIT_PLAYER
3556   if (options.debug)
3557   {
3558     printf("Player status at level initialization:\n");
3559
3560     for (i = 0; i < MAX_PLAYERS; i++)
3561     {
3562       struct PlayerInfo *player = &stored_player[i];
3563
3564       printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3565              i + 1,
3566              player->present,
3567              player->connected,
3568              player->connected_locally,
3569              player->connected_network,
3570              player->active);
3571
3572       if (local_player == player)
3573         printf(" (local player)");
3574
3575       printf("\n");
3576     }
3577   }
3578 #endif
3579
3580   SCAN_PLAYFIELD(x, y)
3581   {
3582     Feld[x][y] = level.field[x][y];
3583     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3584     ChangeDelay[x][y] = 0;
3585     ChangePage[x][y] = -1;
3586     CustomValue[x][y] = 0;              /* initialized in InitField() */
3587     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3588     AmoebaNr[x][y] = 0;
3589     WasJustMoving[x][y] = 0;
3590     WasJustFalling[x][y] = 0;
3591     CheckCollision[x][y] = 0;
3592     CheckImpact[x][y] = 0;
3593     Stop[x][y] = FALSE;
3594     Pushed[x][y] = FALSE;
3595
3596     ChangeCount[x][y] = 0;
3597     ChangeEvent[x][y] = -1;
3598
3599     ExplodePhase[x][y] = 0;
3600     ExplodeDelay[x][y] = 0;
3601     ExplodeField[x][y] = EX_TYPE_NONE;
3602
3603     RunnerVisit[x][y] = 0;
3604     PlayerVisit[x][y] = 0;
3605
3606     GfxFrame[x][y] = 0;
3607     GfxRandom[x][y] = INIT_GFX_RANDOM();
3608     GfxElement[x][y] = EL_UNDEFINED;
3609     GfxAction[x][y] = ACTION_DEFAULT;
3610     GfxDir[x][y] = MV_NONE;
3611     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3612   }
3613
3614   SCAN_PLAYFIELD(x, y)
3615   {
3616     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3617       emulate_bd = FALSE;
3618     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3619       emulate_sb = FALSE;
3620     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3621       emulate_sp = FALSE;
3622
3623     InitField(x, y, TRUE);
3624
3625     ResetGfxAnimation(x, y);
3626   }
3627
3628   InitBeltMovement();
3629
3630   for (i = 0; i < MAX_PLAYERS; i++)
3631   {
3632     struct PlayerInfo *player = &stored_player[i];
3633
3634     /* set number of special actions for bored and sleeping animation */
3635     player->num_special_action_bored =
3636       get_num_special_action(player->artwork_element,
3637                              ACTION_BORING_1, ACTION_BORING_LAST);
3638     player->num_special_action_sleeping =
3639       get_num_special_action(player->artwork_element,
3640                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3641   }
3642
3643   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3644                     emulate_sb ? EMU_SOKOBAN :
3645                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3646
3647   /* initialize type of slippery elements */
3648   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3649   {
3650     if (!IS_CUSTOM_ELEMENT(i))
3651     {
3652       /* default: elements slip down either to the left or right randomly */
3653       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3654
3655       /* SP style elements prefer to slip down on the left side */
3656       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3657         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3658
3659       /* BD style elements prefer to slip down on the left side */
3660       if (game.emulation == EMU_BOULDERDASH)
3661         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3662     }
3663   }
3664
3665   /* initialize explosion and ignition delay */
3666   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3667   {
3668     if (!IS_CUSTOM_ELEMENT(i))
3669     {
3670       int num_phase = 8;
3671       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3672                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3673                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3674       int last_phase = (num_phase + 1) * delay;
3675       int half_phase = (num_phase / 2) * delay;
3676
3677       element_info[i].explosion_delay = last_phase - 1;
3678       element_info[i].ignition_delay = half_phase;
3679
3680       if (i == EL_BLACK_ORB)
3681         element_info[i].ignition_delay = 1;
3682     }
3683   }
3684
3685   /* correct non-moving belts to start moving left */
3686   for (i = 0; i < NUM_BELTS; i++)
3687     if (game.belt_dir[i] == MV_NONE)
3688       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3689
3690 #if USE_NEW_PLAYER_ASSIGNMENTS
3691   for (i = 0; i < MAX_PLAYERS; i++)
3692   {
3693     stored_player[i].connected = FALSE;
3694
3695     /* in network game mode, the local player might not be the first player */
3696     if (stored_player[i].connected_locally)
3697       local_player = &stored_player[i];
3698   }
3699
3700   if (!network.enabled)
3701     local_player->connected = TRUE;
3702
3703   if (tape.playing)
3704   {
3705     for (i = 0; i < MAX_PLAYERS; i++)
3706       stored_player[i].connected = tape.player_participates[i];
3707   }
3708   else if (network.enabled)
3709   {
3710     /* add team mode players connected over the network (needed for correct
3711        assignment of player figures from level to locally playing players) */
3712
3713     for (i = 0; i < MAX_PLAYERS; i++)
3714       if (stored_player[i].connected_network)
3715         stored_player[i].connected = TRUE;
3716   }
3717   else if (game.team_mode)
3718   {
3719     /* try to guess locally connected team mode players (needed for correct
3720        assignment of player figures from level to locally playing players) */
3721
3722     for (i = 0; i < MAX_PLAYERS; i++)
3723       if (setup.input[i].use_joystick ||
3724           setup.input[i].key.left != KSYM_UNDEFINED)
3725         stored_player[i].connected = TRUE;
3726   }
3727
3728 #if DEBUG_INIT_PLAYER
3729   if (options.debug)
3730   {
3731     printf("Player status after level initialization:\n");
3732
3733     for (i = 0; i < MAX_PLAYERS; i++)
3734     {
3735       struct PlayerInfo *player = &stored_player[i];
3736
3737       printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3738              i + 1,
3739              player->present,
3740              player->connected,
3741              player->connected_locally,
3742              player->connected_network,
3743              player->active);
3744
3745       if (local_player == player)
3746         printf(" (local player)");
3747
3748       printf("\n");
3749     }
3750   }
3751 #endif
3752
3753 #if DEBUG_INIT_PLAYER
3754   if (options.debug)
3755     printf("Reassigning players ...\n");
3756 #endif
3757
3758   /* check if any connected player was not found in playfield */
3759   for (i = 0; i < MAX_PLAYERS; i++)
3760   {
3761     struct PlayerInfo *player = &stored_player[i];
3762
3763     if (player->connected && !player->present)
3764     {
3765       struct PlayerInfo *field_player = NULL;
3766
3767 #if DEBUG_INIT_PLAYER
3768       if (options.debug)
3769         printf("- looking for field player for player %d ...\n", i + 1);
3770 #endif
3771
3772       /* assign first free player found that is present in the playfield */
3773
3774       /* first try: look for unmapped playfield player that is not connected */
3775       for (j = 0; j < MAX_PLAYERS; j++)
3776         if (field_player == NULL &&
3777             stored_player[j].present &&
3778             !stored_player[j].mapped &&
3779             !stored_player[j].connected)
3780           field_player = &stored_player[j];
3781
3782       /* second try: look for *any* unmapped playfield player */
3783       for (j = 0; j < MAX_PLAYERS; j++)
3784         if (field_player == NULL &&
3785             stored_player[j].present &&
3786             !stored_player[j].mapped)
3787           field_player = &stored_player[j];
3788
3789       if (field_player != NULL)
3790       {
3791         int jx = field_player->jx, jy = field_player->jy;
3792
3793 #if DEBUG_INIT_PLAYER
3794         if (options.debug)
3795           printf("- found player %d\n", field_player->index_nr + 1);
3796 #endif
3797
3798         player->present = FALSE;
3799         player->active = FALSE;
3800
3801         field_player->present = TRUE;
3802         field_player->active = TRUE;
3803
3804         /*
3805         player->initial_element = field_player->initial_element;
3806         player->artwork_element = field_player->artwork_element;
3807
3808         player->block_last_field       = field_player->block_last_field;
3809         player->block_delay_adjustment = field_player->block_delay_adjustment;
3810         */
3811
3812         StorePlayer[jx][jy] = field_player->element_nr;
3813
3814         field_player->jx = field_player->last_jx = jx;
3815         field_player->jy = field_player->last_jy = jy;
3816
3817         if (local_player == player)
3818           local_player = field_player;
3819
3820         map_player_action[field_player->index_nr] = i;
3821
3822         field_player->mapped = TRUE;
3823
3824 #if DEBUG_INIT_PLAYER
3825         if (options.debug)
3826           printf("- map_player_action[%d] == %d\n",
3827                  field_player->index_nr + 1, i + 1);
3828 #endif
3829       }
3830     }
3831
3832     if (player->connected && player->present)
3833       player->mapped = TRUE;
3834   }
3835
3836 #if DEBUG_INIT_PLAYER
3837   if (options.debug)
3838   {
3839     printf("Player status after player assignment (first stage):\n");
3840
3841     for (i = 0; i < MAX_PLAYERS; i++)
3842     {
3843       struct PlayerInfo *player = &stored_player[i];
3844
3845       printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3846              i + 1,
3847              player->present,
3848              player->connected,
3849              player->connected_locally,
3850              player->connected_network,
3851              player->active);
3852
3853       if (local_player == player)
3854         printf(" (local player)");
3855
3856       printf("\n");
3857     }
3858   }
3859 #endif
3860
3861 #else
3862
3863   /* check if any connected player was not found in playfield */
3864   for (i = 0; i < MAX_PLAYERS; i++)
3865   {
3866     struct PlayerInfo *player = &stored_player[i];
3867
3868     if (player->connected && !player->present)
3869     {
3870       for (j = 0; j < MAX_PLAYERS; j++)
3871       {
3872         struct PlayerInfo *field_player = &stored_player[j];
3873         int jx = field_player->jx, jy = field_player->jy;
3874
3875         /* assign first free player found that is present in the playfield */
3876         if (field_player->present && !field_player->connected)
3877         {
3878           player->present = TRUE;
3879           player->active = TRUE;
3880
3881           field_player->present = FALSE;
3882           field_player->active = FALSE;
3883
3884           player->initial_element = field_player->initial_element;
3885           player->artwork_element = field_player->artwork_element;
3886
3887           player->block_last_field       = field_player->block_last_field;
3888           player->block_delay_adjustment = field_player->block_delay_adjustment;
3889
3890           StorePlayer[jx][jy] = player->element_nr;
3891
3892           player->jx = player->last_jx = jx;
3893           player->jy = player->last_jy = jy;
3894
3895           break;
3896         }
3897       }
3898     }
3899   }
3900 #endif
3901
3902 #if 0
3903   printf("::: local_player->present == %d\n", local_player->present);
3904 #endif
3905
3906   /* set focus to local player for network games, else to all players */
3907   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3908   game.centered_player_nr_next = game.centered_player_nr;
3909   game.set_centered_player = FALSE;
3910
3911   if (network_playing && tape.recording)
3912   {
3913     /* store client dependent player focus when recording network games */
3914     tape.centered_player_nr_next = game.centered_player_nr_next;
3915     tape.set_centered_player = TRUE;
3916   }
3917
3918   if (tape.playing)
3919   {
3920     /* when playing a tape, eliminate all players who do not participate */
3921
3922 #if USE_NEW_PLAYER_ASSIGNMENTS
3923
3924     if (!game.team_mode)
3925     {
3926       for (i = 0; i < MAX_PLAYERS; i++)
3927       {
3928         if (stored_player[i].active &&
3929             !tape.player_participates[map_player_action[i]])
3930         {
3931           struct PlayerInfo *player = &stored_player[i];
3932           int jx = player->jx, jy = player->jy;
3933
3934 #if DEBUG_INIT_PLAYER
3935           if (options.debug)
3936             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3937 #endif
3938
3939           player->active = FALSE;
3940           StorePlayer[jx][jy] = 0;
3941           Feld[jx][jy] = EL_EMPTY;
3942         }
3943       }
3944     }
3945
3946 #else
3947
3948     for (i = 0; i < MAX_PLAYERS; i++)
3949     {
3950       if (stored_player[i].active &&
3951           !tape.player_participates[i])
3952       {
3953         struct PlayerInfo *player = &stored_player[i];
3954         int jx = player->jx, jy = player->jy;
3955
3956         player->active = FALSE;
3957         StorePlayer[jx][jy] = 0;
3958         Feld[jx][jy] = EL_EMPTY;
3959       }
3960     }
3961 #endif
3962   }
3963   else if (!network.enabled && !game.team_mode)         /* && !tape.playing */
3964   {
3965     /* when in single player mode, eliminate all but the local player */
3966
3967     for (i = 0; i < MAX_PLAYERS; i++)
3968     {
3969       struct PlayerInfo *player = &stored_player[i];
3970
3971       if (player->active && player != local_player)
3972       {
3973         int jx = player->jx, jy = player->jy;
3974
3975         player->active = FALSE;
3976         player->present = FALSE;
3977
3978         StorePlayer[jx][jy] = 0;
3979         Feld[jx][jy] = EL_EMPTY;
3980       }
3981     }
3982   }
3983
3984   /* when recording the game, store which players take part in the game */
3985   if (tape.recording)
3986   {
3987 #if USE_NEW_PLAYER_ASSIGNMENTS
3988     for (i = 0; i < MAX_PLAYERS; i++)
3989       if (stored_player[i].connected)
3990         tape.player_participates[i] = TRUE;
3991 #else
3992     for (i = 0; i < MAX_PLAYERS; i++)
3993       if (stored_player[i].active)
3994         tape.player_participates[i] = TRUE;
3995 #endif
3996   }
3997
3998 #if DEBUG_INIT_PLAYER
3999   if (options.debug)
4000   {
4001     printf("Player status after player assignment (final stage):\n");
4002
4003     for (i = 0; i < MAX_PLAYERS; i++)
4004     {
4005       struct PlayerInfo *player = &stored_player[i];
4006
4007       printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
4008              i + 1,
4009              player->present,
4010              player->connected,
4011              player->connected_locally,
4012              player->connected_network,
4013              player->active);
4014
4015       if (local_player == player)
4016         printf(" (local player)");
4017
4018       printf("\n");
4019     }
4020   }
4021 #endif
4022
4023   if (BorderElement == EL_EMPTY)
4024   {
4025     SBX_Left = 0;
4026     SBX_Right = lev_fieldx - SCR_FIELDX;
4027     SBY_Upper = 0;
4028     SBY_Lower = lev_fieldy - SCR_FIELDY;
4029   }
4030   else
4031   {
4032     SBX_Left = -1;
4033     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4034     SBY_Upper = -1;
4035     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4036   }
4037
4038   if (full_lev_fieldx <= SCR_FIELDX)
4039     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4040   if (full_lev_fieldy <= SCR_FIELDY)
4041     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4042
4043   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4044     SBX_Left--;
4045   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4046     SBY_Upper--;
4047
4048   /* if local player not found, look for custom element that might create
4049      the player (make some assumptions about the right custom element) */
4050   if (!local_player->present)
4051   {
4052     int start_x = 0, start_y = 0;
4053     int found_rating = 0;
4054     int found_element = EL_UNDEFINED;
4055     int player_nr = local_player->index_nr;
4056
4057     SCAN_PLAYFIELD(x, y)
4058     {
4059       int element = Feld[x][y];
4060       int content;
4061       int xx, yy;
4062       boolean is_player;
4063
4064       if (level.use_start_element[player_nr] &&
4065           level.start_element[player_nr] == element &&
4066           found_rating < 4)
4067       {
4068         start_x = x;
4069         start_y = y;
4070
4071         found_rating = 4;
4072         found_element = element;
4073       }
4074
4075       if (!IS_CUSTOM_ELEMENT(element))
4076         continue;
4077
4078       if (CAN_CHANGE(element))
4079       {
4080         for (i = 0; i < element_info[element].num_change_pages; i++)
4081         {
4082           /* check for player created from custom element as single target */
4083           content = element_info[element].change_page[i].target_element;
4084           is_player = ELEM_IS_PLAYER(content);
4085
4086           if (is_player && (found_rating < 3 ||
4087                             (found_rating == 3 && element < found_element)))
4088           {
4089             start_x = x;
4090             start_y = y;
4091
4092             found_rating = 3;
4093             found_element = element;
4094           }
4095         }
4096       }
4097
4098       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4099       {
4100         /* check for player created from custom element as explosion content */
4101         content = element_info[element].content.e[xx][yy];
4102         is_player = ELEM_IS_PLAYER(content);
4103
4104         if (is_player && (found_rating < 2 ||
4105                           (found_rating == 2 && element < found_element)))
4106         {
4107           start_x = x + xx - 1;
4108           start_y = y + yy - 1;
4109
4110           found_rating = 2;
4111           found_element = element;
4112         }
4113
4114         if (!CAN_CHANGE(element))
4115           continue;
4116
4117         for (i = 0; i < element_info[element].num_change_pages; i++)
4118         {
4119           /* check for player created from custom element as extended target */
4120           content =
4121             element_info[element].change_page[i].target_content.e[xx][yy];
4122
4123           is_player = ELEM_IS_PLAYER(content);
4124
4125           if (is_player && (found_rating < 1 ||
4126                             (found_rating == 1 && element < found_element)))
4127           {
4128             start_x = x + xx - 1;
4129             start_y = y + yy - 1;
4130
4131             found_rating = 1;
4132             found_element = element;
4133           }
4134         }
4135       }
4136     }
4137
4138     scroll_x = SCROLL_POSITION_X(start_x);
4139     scroll_y = SCROLL_POSITION_Y(start_y);
4140   }
4141   else
4142   {
4143     scroll_x = SCROLL_POSITION_X(local_player->jx);
4144     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4145   }
4146
4147   /* !!! FIX THIS (START) !!! */
4148   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4149   {
4150     InitGameEngine_EM();
4151   }
4152   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4153   {
4154     InitGameEngine_SP();
4155   }
4156   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4157   {
4158     InitGameEngine_MM();
4159   }
4160   else
4161   {
4162     DrawLevel(REDRAW_FIELD);
4163     DrawAllPlayers();
4164
4165     /* after drawing the level, correct some elements */
4166     if (game.timegate_time_left == 0)
4167       CloseAllOpenTimegates();
4168   }
4169
4170   /* blit playfield from scroll buffer to normal back buffer for fading in */
4171   BlitScreenToBitmap(backbuffer);
4172   /* !!! FIX THIS (END) !!! */
4173
4174   DrawMaskedBorder(fade_mask);
4175
4176   FadeIn(fade_mask);
4177
4178 #if 1
4179   // full screen redraw is required at this point in the following cases:
4180   // - special editor door undrawn when game was started from level editor
4181   // - drawing area (playfield) was changed and has to be removed completely
4182   redraw_mask = REDRAW_ALL;
4183   BackToFront();
4184 #endif
4185
4186   if (!game.restart_level)
4187   {
4188     /* copy default game door content to main double buffer */
4189
4190     /* !!! CHECK AGAIN !!! */
4191     SetPanelBackground();
4192     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4193     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4194   }
4195
4196   SetPanelBackground();
4197   SetDrawBackgroundMask(REDRAW_DOOR_1);
4198
4199   UpdateAndDisplayGameControlValues();
4200
4201   if (!game.restart_level)
4202   {
4203     UnmapGameButtons();
4204     UnmapTapeButtons();
4205
4206     FreeGameButtons();
4207     CreateGameButtons();
4208
4209     MapGameButtons();
4210     MapTapeButtons();
4211
4212     /* copy actual game door content to door double buffer for OpenDoor() */
4213     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4214
4215     OpenDoor(DOOR_OPEN_ALL);
4216
4217     KeyboardAutoRepeatOffUnlessAutoplay();
4218
4219 #if DEBUG_INIT_PLAYER
4220     if (options.debug)
4221     {
4222       printf("Player status (final):\n");
4223
4224       for (i = 0; i < MAX_PLAYERS; i++)
4225       {
4226         struct PlayerInfo *player = &stored_player[i];
4227
4228         printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
4229                i + 1,
4230                player->present,
4231                player->connected,
4232                player->connected_locally,
4233                player->connected_network,
4234                player->active);
4235
4236         if (local_player == player)
4237           printf(" (local player)");
4238
4239         printf("\n");
4240       }
4241     }
4242 #endif
4243   }
4244
4245   UnmapAllGadgets();
4246
4247   MapGameButtons();
4248   MapTapeButtons();
4249
4250   if (!game.restart_level && !tape.playing)
4251   {
4252     LevelStats_incPlayed(level_nr);
4253
4254     SaveLevelSetup_SeriesInfo();
4255   }
4256
4257   game.restart_level = FALSE;
4258   game.restart_game_message = NULL;
4259
4260   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4261     InitGameActions_MM();
4262
4263   SaveEngineSnapshotToListInitial();
4264
4265   if (!game.restart_level)
4266   {
4267     PlaySound(SND_GAME_STARTING);
4268
4269     if (setup.sound_music)
4270       PlayLevelMusic();
4271   }
4272 }
4273
4274 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4275                         int actual_player_x, int actual_player_y)
4276 {
4277   /* this is used for non-R'n'D game engines to update certain engine values */
4278
4279   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4280   {
4281     actual_player_x = correctLevelPosX_EM(actual_player_x);
4282     actual_player_y = correctLevelPosY_EM(actual_player_y);
4283   }
4284
4285   /* needed to determine if sounds are played within the visible screen area */
4286   scroll_x = actual_scroll_x;
4287   scroll_y = actual_scroll_y;
4288
4289   /* needed to get player position for "follow finger" playing input method */
4290   local_player->jx = actual_player_x;
4291   local_player->jy = actual_player_y;
4292 }
4293
4294 void InitMovDir(int x, int y)
4295 {
4296   int i, element = Feld[x][y];
4297   static int xy[4][2] =
4298   {
4299     {  0, +1 },
4300     { +1,  0 },
4301     {  0, -1 },
4302     { -1,  0 }
4303   };
4304   static int direction[3][4] =
4305   {
4306     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4307     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4308     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4309   };
4310
4311   switch (element)
4312   {
4313     case EL_BUG_RIGHT:
4314     case EL_BUG_UP:
4315     case EL_BUG_LEFT:
4316     case EL_BUG_DOWN:
4317       Feld[x][y] = EL_BUG;
4318       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4319       break;
4320
4321     case EL_SPACESHIP_RIGHT:
4322     case EL_SPACESHIP_UP:
4323     case EL_SPACESHIP_LEFT:
4324     case EL_SPACESHIP_DOWN:
4325       Feld[x][y] = EL_SPACESHIP;
4326       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4327       break;
4328
4329     case EL_BD_BUTTERFLY_RIGHT:
4330     case EL_BD_BUTTERFLY_UP:
4331     case EL_BD_BUTTERFLY_LEFT:
4332     case EL_BD_BUTTERFLY_DOWN:
4333       Feld[x][y] = EL_BD_BUTTERFLY;
4334       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4335       break;
4336
4337     case EL_BD_FIREFLY_RIGHT:
4338     case EL_BD_FIREFLY_UP:
4339     case EL_BD_FIREFLY_LEFT:
4340     case EL_BD_FIREFLY_DOWN:
4341       Feld[x][y] = EL_BD_FIREFLY;
4342       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4343       break;
4344
4345     case EL_PACMAN_RIGHT:
4346     case EL_PACMAN_UP:
4347     case EL_PACMAN_LEFT:
4348     case EL_PACMAN_DOWN:
4349       Feld[x][y] = EL_PACMAN;
4350       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4351       break;
4352
4353     case EL_YAMYAM_LEFT:
4354     case EL_YAMYAM_RIGHT:
4355     case EL_YAMYAM_UP:
4356     case EL_YAMYAM_DOWN:
4357       Feld[x][y] = EL_YAMYAM;
4358       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4359       break;
4360
4361     case EL_SP_SNIKSNAK:
4362       MovDir[x][y] = MV_UP;
4363       break;
4364
4365     case EL_SP_ELECTRON:
4366       MovDir[x][y] = MV_LEFT;
4367       break;
4368
4369     case EL_MOLE_LEFT:
4370     case EL_MOLE_RIGHT:
4371     case EL_MOLE_UP:
4372     case EL_MOLE_DOWN:
4373       Feld[x][y] = EL_MOLE;
4374       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4375       break;
4376
4377     default:
4378       if (IS_CUSTOM_ELEMENT(element))
4379       {
4380         struct ElementInfo *ei = &element_info[element];
4381         int move_direction_initial = ei->move_direction_initial;
4382         int move_pattern = ei->move_pattern;
4383
4384         if (move_direction_initial == MV_START_PREVIOUS)
4385         {
4386           if (MovDir[x][y] != MV_NONE)
4387             return;
4388
4389           move_direction_initial = MV_START_AUTOMATIC;
4390         }
4391
4392         if (move_direction_initial == MV_START_RANDOM)
4393           MovDir[x][y] = 1 << RND(4);
4394         else if (move_direction_initial & MV_ANY_DIRECTION)
4395           MovDir[x][y] = move_direction_initial;
4396         else if (move_pattern == MV_ALL_DIRECTIONS ||
4397                  move_pattern == MV_TURNING_LEFT ||
4398                  move_pattern == MV_TURNING_RIGHT ||
4399                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4400                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4401                  move_pattern == MV_TURNING_RANDOM)
4402           MovDir[x][y] = 1 << RND(4);
4403         else if (move_pattern == MV_HORIZONTAL)
4404           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4405         else if (move_pattern == MV_VERTICAL)
4406           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4407         else if (move_pattern & MV_ANY_DIRECTION)
4408           MovDir[x][y] = element_info[element].move_pattern;
4409         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4410                  move_pattern == MV_ALONG_RIGHT_SIDE)
4411         {
4412           /* use random direction as default start direction */
4413           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4414             MovDir[x][y] = 1 << RND(4);
4415
4416           for (i = 0; i < NUM_DIRECTIONS; i++)
4417           {
4418             int x1 = x + xy[i][0];
4419             int y1 = y + xy[i][1];
4420
4421             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4422             {
4423               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4424                 MovDir[x][y] = direction[0][i];
4425               else
4426                 MovDir[x][y] = direction[1][i];
4427
4428               break;
4429             }
4430           }
4431         }                
4432       }
4433       else
4434       {
4435         MovDir[x][y] = 1 << RND(4);
4436
4437         if (element != EL_BUG &&
4438             element != EL_SPACESHIP &&
4439             element != EL_BD_BUTTERFLY &&
4440             element != EL_BD_FIREFLY)
4441           break;
4442
4443         for (i = 0; i < NUM_DIRECTIONS; i++)
4444         {
4445           int x1 = x + xy[i][0];
4446           int y1 = y + xy[i][1];
4447
4448           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4449           {
4450             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4451             {
4452               MovDir[x][y] = direction[0][i];
4453               break;
4454             }
4455             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4456                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4457             {
4458               MovDir[x][y] = direction[1][i];
4459               break;
4460             }
4461           }
4462         }
4463       }
4464       break;
4465   }
4466
4467   GfxDir[x][y] = MovDir[x][y];
4468 }
4469
4470 void InitAmoebaNr(int x, int y)
4471 {
4472   int i;
4473   int group_nr = AmoebeNachbarNr(x, y);
4474
4475   if (group_nr == 0)
4476   {
4477     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4478     {
4479       if (AmoebaCnt[i] == 0)
4480       {
4481         group_nr = i;
4482         break;
4483       }
4484     }
4485   }
4486
4487   AmoebaNr[x][y] = group_nr;
4488   AmoebaCnt[group_nr]++;
4489   AmoebaCnt2[group_nr]++;
4490 }
4491
4492 static void PlayerWins(struct PlayerInfo *player)
4493 {
4494   player->LevelSolved = TRUE;
4495   player->GameOver = TRUE;
4496
4497   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4498                          level.native_em_level->lev->score :
4499                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4500                          game_mm.score :
4501                          player->score);
4502   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4503                           MM_HEALTH(game_mm.laser_overload_value) :
4504                           player->health);
4505
4506   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4507                                       TimeLeft);
4508   player->LevelSolved_CountingScore = player->score_final;
4509   player->LevelSolved_CountingHealth = player->health_final;
4510 }
4511
4512 void GameWon()
4513 {
4514   static int time_count_steps;
4515   static int time, time_final;
4516   static int score, score_final;
4517   static int health, health_final;
4518   static int game_over_delay_1 = 0;
4519   static int game_over_delay_2 = 0;
4520   static int game_over_delay_3 = 0;
4521   int game_over_delay_value_1 = 50;
4522   int game_over_delay_value_2 = 25;
4523   int game_over_delay_value_3 = 50;
4524
4525   if (!local_player->LevelSolved_GameWon)
4526   {
4527     int i;
4528
4529     /* do not start end game actions before the player stops moving (to exit) */
4530     if (local_player->MovPos)
4531       return;
4532
4533     local_player->LevelSolved_GameWon = TRUE;
4534     local_player->LevelSolved_SaveTape = tape.recording;
4535     local_player->LevelSolved_SaveScore = !tape.playing;
4536
4537     if (!tape.playing)
4538     {
4539       LevelStats_incSolved(level_nr);
4540
4541       SaveLevelSetup_SeriesInfo();
4542     }
4543
4544     if (tape.auto_play)         /* tape might already be stopped here */
4545       tape.auto_play_level_solved = TRUE;
4546
4547     TapeStop();
4548
4549     game_over_delay_1 = 0;
4550     game_over_delay_2 = 0;
4551     game_over_delay_3 = game_over_delay_value_3;
4552
4553     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4554     score = score_final = local_player->score_final;
4555     health = health_final = local_player->health_final;
4556
4557     if (level.score[SC_TIME_BONUS] > 0)
4558     {
4559       if (TimeLeft > 0)
4560       {
4561         time_final = 0;
4562         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4563       }
4564       else if (game.no_time_limit && TimePlayed < 999)
4565       {
4566         time_final = 999;
4567         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4568       }
4569
4570       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4571
4572       game_over_delay_1 = game_over_delay_value_1;
4573
4574       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4575       {
4576         health_final = 0;
4577         score_final += health * level.score[SC_TIME_BONUS];
4578
4579         game_over_delay_2 = game_over_delay_value_2;
4580       }
4581
4582       local_player->score_final = score_final;
4583       local_player->health_final = health_final;
4584     }
4585
4586     if (level_editor_test_game)
4587     {
4588       time = time_final;
4589       score = score_final;
4590
4591       local_player->LevelSolved_CountingTime = time;
4592       local_player->LevelSolved_CountingScore = score;
4593
4594       game_panel_controls[GAME_PANEL_TIME].value = time;
4595       game_panel_controls[GAME_PANEL_SCORE].value = score;
4596
4597       DisplayGameControlValues();
4598     }
4599
4600     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4601     {
4602       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4603       {
4604         /* close exit door after last player */
4605         if ((AllPlayersGone &&
4606              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4607               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4608               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4609             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4610             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4611         {
4612           int element = Feld[ExitX][ExitY];
4613
4614           Feld[ExitX][ExitY] =
4615             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4616              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4617              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4618              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4619              EL_EM_STEEL_EXIT_CLOSING);
4620
4621           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4622         }
4623
4624         /* player disappears */
4625         DrawLevelField(ExitX, ExitY);
4626       }
4627
4628       for (i = 0; i < MAX_PLAYERS; i++)
4629       {
4630         struct PlayerInfo *player = &stored_player[i];
4631
4632         if (player->present)
4633         {
4634           RemovePlayer(player);
4635
4636           /* player disappears */
4637           DrawLevelField(player->jx, player->jy);
4638         }
4639       }
4640     }
4641
4642     PlaySound(SND_GAME_WINNING);
4643   }
4644
4645   if (game_over_delay_1 > 0)
4646   {
4647     game_over_delay_1--;
4648
4649     return;
4650   }
4651
4652   if (time != time_final)
4653   {
4654     int time_to_go = ABS(time_final - time);
4655     int time_count_dir = (time < time_final ? +1 : -1);
4656
4657     if (time_to_go < time_count_steps)
4658       time_count_steps = 1;
4659
4660     time  += time_count_steps * time_count_dir;
4661     score += time_count_steps * level.score[SC_TIME_BONUS];
4662
4663     local_player->LevelSolved_CountingTime = time;
4664     local_player->LevelSolved_CountingScore = score;
4665
4666     game_panel_controls[GAME_PANEL_TIME].value = time;
4667     game_panel_controls[GAME_PANEL_SCORE].value = score;
4668
4669     DisplayGameControlValues();
4670
4671     if (time == time_final)
4672       StopSound(SND_GAME_LEVELTIME_BONUS);
4673     else if (setup.sound_loops)
4674       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4675     else
4676       PlaySound(SND_GAME_LEVELTIME_BONUS);
4677
4678     return;
4679   }
4680
4681   if (game_over_delay_2 > 0)
4682   {
4683     game_over_delay_2--;
4684
4685     return;
4686   }
4687
4688   if (health != health_final)
4689   {
4690     int health_count_dir = (health < health_final ? +1 : -1);
4691
4692     health += health_count_dir;
4693     score  += level.score[SC_TIME_BONUS];
4694
4695     local_player->LevelSolved_CountingHealth = health;
4696     local_player->LevelSolved_CountingScore = score;
4697
4698     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4699     game_panel_controls[GAME_PANEL_SCORE].value = score;
4700
4701     DisplayGameControlValues();
4702
4703     if (health == health_final)
4704       StopSound(SND_GAME_LEVELTIME_BONUS);
4705     else if (setup.sound_loops)
4706       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4707     else
4708       PlaySound(SND_GAME_LEVELTIME_BONUS);
4709
4710     return;
4711   }
4712
4713   local_player->LevelSolved_PanelOff = TRUE;
4714
4715   if (game_over_delay_3 > 0)
4716   {
4717     game_over_delay_3--;
4718
4719     return;
4720   }
4721
4722   GameEnd();
4723 }
4724
4725 void GameEnd()
4726 {
4727   int hi_pos;
4728   boolean raise_level = FALSE;
4729
4730   local_player->LevelSolved_GameEnd = TRUE;
4731
4732   if (local_player->LevelSolved_SaveTape)
4733   {
4734     /* make sure that request dialog to save tape does not open door again */
4735     if (!global.use_envelope_request)
4736       CloseDoor(DOOR_CLOSE_1);
4737
4738     SaveTapeChecked_LevelSolved(tape.level_nr);         /* ask to save tape */
4739   }
4740
4741   /* if no tape is to be saved, close both doors simultaneously */
4742   CloseDoor(DOOR_CLOSE_ALL);
4743
4744   if (level_editor_test_game)
4745   {
4746     SetGameStatus(GAME_MODE_MAIN);
4747
4748     DrawMainMenu();
4749
4750     return;
4751   }
4752
4753   if (!local_player->LevelSolved_SaveScore)
4754   {
4755     SetGameStatus(GAME_MODE_MAIN);
4756
4757     DrawMainMenu();
4758
4759     return;
4760   }
4761
4762   if (level_nr == leveldir_current->handicap_level)
4763   {
4764     leveldir_current->handicap_level++;
4765
4766     SaveLevelSetup_SeriesInfo();
4767   }
4768
4769   if (setup.increment_levels &&
4770       level_nr < leveldir_current->last_level)
4771     raise_level = TRUE;                 /* advance to next level */
4772
4773   if ((hi_pos = NewHiScore()) >= 0) 
4774   {
4775     SetGameStatus(GAME_MODE_SCORES);
4776
4777     DrawHallOfFame(hi_pos);
4778
4779     if (raise_level)
4780     {
4781       level_nr++;
4782       TapeErase();
4783     }
4784   }
4785   else
4786   {
4787     SetGameStatus(GAME_MODE_MAIN);
4788
4789     if (raise_level)
4790     {
4791       level_nr++;
4792       TapeErase();
4793     }
4794
4795     DrawMainMenu();
4796   }
4797 }
4798
4799 int NewHiScore()
4800 {
4801   int k, l;
4802   int position = -1;
4803   boolean one_score_entry_per_name = !program.many_scores_per_name;
4804
4805   LoadScore(level_nr);
4806
4807   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4808       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4809     return -1;
4810
4811   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4812   {
4813     if (local_player->score_final > highscore[k].Score)
4814     {
4815       /* player has made it to the hall of fame */
4816
4817       if (k < MAX_SCORE_ENTRIES - 1)
4818       {
4819         int m = MAX_SCORE_ENTRIES - 1;
4820
4821         if (one_score_entry_per_name)
4822         {
4823           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4824             if (strEqual(setup.player_name, highscore[l].Name))
4825               m = l;
4826
4827           if (m == k)   /* player's new highscore overwrites his old one */
4828             goto put_into_list;
4829         }
4830
4831         for (l = m; l > k; l--)
4832         {
4833           strcpy(highscore[l].Name, highscore[l - 1].Name);
4834           highscore[l].Score = highscore[l - 1].Score;
4835         }
4836       }
4837
4838       put_into_list:
4839
4840       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4841       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4842       highscore[k].Score = local_player->score_final; 
4843       position = k;
4844
4845       break;
4846     }
4847     else if (one_score_entry_per_name &&
4848              !strncmp(setup.player_name, highscore[k].Name,
4849                       MAX_PLAYER_NAME_LEN))
4850       break;    /* player already there with a higher score */
4851   }
4852
4853   if (position >= 0) 
4854     SaveScore(level_nr);
4855
4856   return position;
4857 }
4858
4859 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4860 {
4861   int element = Feld[x][y];
4862   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4863   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4864   int horiz_move = (dx != 0);
4865   int sign = (horiz_move ? dx : dy);
4866   int step = sign * element_info[element].move_stepsize;
4867
4868   /* special values for move stepsize for spring and things on conveyor belt */
4869   if (horiz_move)
4870   {
4871     if (CAN_FALL(element) &&
4872         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4873       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4874     else if (element == EL_SPRING)
4875       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4876   }
4877
4878   return step;
4879 }
4880
4881 inline static int getElementMoveStepsize(int x, int y)
4882 {
4883   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4884 }
4885
4886 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4887 {
4888   if (player->GfxAction != action || player->GfxDir != dir)
4889   {
4890     player->GfxAction = action;
4891     player->GfxDir = dir;
4892     player->Frame = 0;
4893     player->StepFrame = 0;
4894   }
4895 }
4896
4897 static void ResetGfxFrame(int x, int y)
4898 {
4899   // profiling showed that "autotest" spends 10~20% of its time in this function
4900   if (DrawingDeactivatedField())
4901     return;
4902
4903   int element = Feld[x][y];
4904   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4905
4906   if (graphic_info[graphic].anim_global_sync)
4907     GfxFrame[x][y] = FrameCounter;
4908   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4909     GfxFrame[x][y] = CustomValue[x][y];
4910   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4911     GfxFrame[x][y] = element_info[element].collect_score;
4912   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4913     GfxFrame[x][y] = ChangeDelay[x][y];
4914 }
4915
4916 static void ResetGfxAnimation(int x, int y)
4917 {
4918   GfxAction[x][y] = ACTION_DEFAULT;
4919   GfxDir[x][y] = MovDir[x][y];
4920   GfxFrame[x][y] = 0;
4921
4922   ResetGfxFrame(x, y);
4923 }
4924
4925 static void ResetRandomAnimationValue(int x, int y)
4926 {
4927   GfxRandom[x][y] = INIT_GFX_RANDOM();
4928 }
4929
4930 void InitMovingField(int x, int y, int direction)
4931 {
4932   int element = Feld[x][y];
4933   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4934   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4935   int newx = x + dx;
4936   int newy = y + dy;
4937   boolean is_moving_before, is_moving_after;
4938
4939   /* check if element was/is moving or being moved before/after mode change */
4940   is_moving_before = (WasJustMoving[x][y] != 0);
4941   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4942
4943   /* reset animation only for moving elements which change direction of moving
4944      or which just started or stopped moving
4945      (else CEs with property "can move" / "not moving" are reset each frame) */
4946   if (is_moving_before != is_moving_after ||
4947       direction != MovDir[x][y])
4948     ResetGfxAnimation(x, y);
4949
4950   MovDir[x][y] = direction;
4951   GfxDir[x][y] = direction;
4952
4953   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4954                      direction == MV_DOWN && CAN_FALL(element) ?
4955                      ACTION_FALLING : ACTION_MOVING);
4956
4957   /* this is needed for CEs with property "can move" / "not moving" */
4958
4959   if (is_moving_after)
4960   {
4961     if (Feld[newx][newy] == EL_EMPTY)
4962       Feld[newx][newy] = EL_BLOCKED;
4963
4964     MovDir[newx][newy] = MovDir[x][y];
4965
4966     CustomValue[newx][newy] = CustomValue[x][y];
4967
4968     GfxFrame[newx][newy] = GfxFrame[x][y];
4969     GfxRandom[newx][newy] = GfxRandom[x][y];
4970     GfxAction[newx][newy] = GfxAction[x][y];
4971     GfxDir[newx][newy] = GfxDir[x][y];
4972   }
4973 }
4974
4975 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4976 {
4977   int direction = MovDir[x][y];
4978   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4979   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4980
4981   *goes_to_x = newx;
4982   *goes_to_y = newy;
4983 }
4984
4985 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4986 {
4987   int oldx = x, oldy = y;
4988   int direction = MovDir[x][y];
4989
4990   if (direction == MV_LEFT)
4991     oldx++;
4992   else if (direction == MV_RIGHT)
4993     oldx--;
4994   else if (direction == MV_UP)
4995     oldy++;
4996   else if (direction == MV_DOWN)
4997     oldy--;
4998
4999   *comes_from_x = oldx;
5000   *comes_from_y = oldy;
5001 }
5002
5003 int MovingOrBlocked2Element(int x, int y)
5004 {
5005   int element = Feld[x][y];
5006
5007   if (element == EL_BLOCKED)
5008   {
5009     int oldx, oldy;
5010
5011     Blocked2Moving(x, y, &oldx, &oldy);
5012     return Feld[oldx][oldy];
5013   }
5014   else
5015     return element;
5016 }
5017
5018 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5019 {
5020   /* like MovingOrBlocked2Element(), but if element is moving
5021      and (x,y) is the field the moving element is just leaving,
5022      return EL_BLOCKED instead of the element value */
5023   int element = Feld[x][y];
5024
5025   if (IS_MOVING(x, y))
5026   {
5027     if (element == EL_BLOCKED)
5028     {
5029       int oldx, oldy;
5030
5031       Blocked2Moving(x, y, &oldx, &oldy);
5032       return Feld[oldx][oldy];
5033     }
5034     else
5035       return EL_BLOCKED;
5036   }
5037   else
5038     return element;
5039 }
5040
5041 static void RemoveField(int x, int y)
5042 {
5043   Feld[x][y] = EL_EMPTY;
5044
5045   MovPos[x][y] = 0;
5046   MovDir[x][y] = 0;
5047   MovDelay[x][y] = 0;
5048
5049   CustomValue[x][y] = 0;
5050
5051   AmoebaNr[x][y] = 0;
5052   ChangeDelay[x][y] = 0;
5053   ChangePage[x][y] = -1;
5054   Pushed[x][y] = FALSE;
5055
5056   GfxElement[x][y] = EL_UNDEFINED;
5057   GfxAction[x][y] = ACTION_DEFAULT;
5058   GfxDir[x][y] = MV_NONE;
5059 }
5060
5061 void RemoveMovingField(int x, int y)
5062 {
5063   int oldx = x, oldy = y, newx = x, newy = y;
5064   int element = Feld[x][y];
5065   int next_element = EL_UNDEFINED;
5066
5067   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5068     return;
5069
5070   if (IS_MOVING(x, y))
5071   {
5072     Moving2Blocked(x, y, &newx, &newy);
5073
5074     if (Feld[newx][newy] != EL_BLOCKED)
5075     {
5076       /* element is moving, but target field is not free (blocked), but
5077          already occupied by something different (example: acid pool);
5078          in this case, only remove the moving field, but not the target */
5079
5080       RemoveField(oldx, oldy);
5081
5082       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5083
5084       TEST_DrawLevelField(oldx, oldy);
5085
5086       return;
5087     }
5088   }
5089   else if (element == EL_BLOCKED)
5090   {
5091     Blocked2Moving(x, y, &oldx, &oldy);
5092     if (!IS_MOVING(oldx, oldy))
5093       return;
5094   }
5095
5096   if (element == EL_BLOCKED &&
5097       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5098        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5099        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5100        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5101        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5102        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5103     next_element = get_next_element(Feld[oldx][oldy]);
5104
5105   RemoveField(oldx, oldy);
5106   RemoveField(newx, newy);
5107
5108   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5109
5110   if (next_element != EL_UNDEFINED)
5111     Feld[oldx][oldy] = next_element;
5112
5113   TEST_DrawLevelField(oldx, oldy);
5114   TEST_DrawLevelField(newx, newy);
5115 }
5116
5117 void DrawDynamite(int x, int y)
5118 {
5119   int sx = SCREENX(x), sy = SCREENY(y);
5120   int graphic = el2img(Feld[x][y]);
5121   int frame;
5122
5123   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5124     return;
5125
5126   if (IS_WALKABLE_INSIDE(Back[x][y]))
5127     return;
5128
5129   if (Back[x][y])
5130     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5131   else if (Store[x][y])
5132     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5133
5134   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5135
5136   if (Back[x][y] || Store[x][y])
5137     DrawGraphicThruMask(sx, sy, graphic, frame);
5138   else
5139     DrawGraphic(sx, sy, graphic, frame);
5140 }
5141
5142 void CheckDynamite(int x, int y)
5143 {
5144   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5145   {
5146     MovDelay[x][y]--;
5147
5148     if (MovDelay[x][y] != 0)
5149     {
5150       DrawDynamite(x, y);
5151       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5152
5153       return;
5154     }
5155   }
5156
5157   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5158
5159   Bang(x, y);
5160 }
5161
5162 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5163 {
5164   boolean num_checked_players = 0;
5165   int i;
5166
5167   for (i = 0; i < MAX_PLAYERS; i++)
5168   {
5169     if (stored_player[i].active)
5170     {
5171       int sx = stored_player[i].jx;
5172       int sy = stored_player[i].jy;
5173
5174       if (num_checked_players == 0)
5175       {
5176         *sx1 = *sx2 = sx;
5177         *sy1 = *sy2 = sy;
5178       }
5179       else
5180       {
5181         *sx1 = MIN(*sx1, sx);
5182         *sy1 = MIN(*sy1, sy);
5183         *sx2 = MAX(*sx2, sx);
5184         *sy2 = MAX(*sy2, sy);
5185       }
5186
5187       num_checked_players++;
5188     }
5189   }
5190 }
5191
5192 static boolean checkIfAllPlayersFitToScreen_RND()
5193 {
5194   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5195
5196   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5197
5198   return (sx2 - sx1 < SCR_FIELDX &&
5199           sy2 - sy1 < SCR_FIELDY);
5200 }
5201
5202 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5203 {
5204   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5205
5206   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5207
5208   *sx = (sx1 + sx2) / 2;
5209   *sy = (sy1 + sy2) / 2;
5210 }
5211
5212 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5213                         boolean center_screen, boolean quick_relocation)
5214 {
5215   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5216   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5217   boolean no_delay = (tape.warp_forward);
5218   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5219   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5220   int new_scroll_x, new_scroll_y;
5221
5222   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5223   {
5224     /* case 1: quick relocation inside visible screen (without scrolling) */
5225
5226     RedrawPlayfield();
5227
5228     return;
5229   }
5230
5231   if (!level.shifted_relocation || center_screen)
5232   {
5233     /* relocation _with_ centering of screen */
5234
5235     new_scroll_x = SCROLL_POSITION_X(x);
5236     new_scroll_y = SCROLL_POSITION_Y(y);
5237   }
5238   else
5239   {
5240     /* relocation _without_ centering of screen */
5241
5242     int center_scroll_x = SCROLL_POSITION_X(old_x);
5243     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5244     int offset_x = x + (scroll_x - center_scroll_x);
5245     int offset_y = y + (scroll_y - center_scroll_y);
5246
5247     /* for new screen position, apply previous offset to center position */
5248     new_scroll_x = SCROLL_POSITION_X(offset_x);
5249     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5250   }
5251
5252   if (quick_relocation)
5253   {
5254     /* case 2: quick relocation (redraw without visible scrolling) */
5255
5256     scroll_x = new_scroll_x;
5257     scroll_y = new_scroll_y;
5258
5259     RedrawPlayfield();
5260
5261     return;
5262   }
5263
5264   /* case 3: visible relocation (with scrolling to new position) */
5265
5266   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5267
5268   SetVideoFrameDelay(wait_delay_value);
5269
5270   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5271   {
5272     int dx = 0, dy = 0;
5273     int fx = FX, fy = FY;
5274
5275     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5276     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5277
5278     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5279       break;
5280
5281     scroll_x -= dx;
5282     scroll_y -= dy;
5283
5284     fx += dx * TILEX / 2;
5285     fy += dy * TILEY / 2;
5286
5287     ScrollLevel(dx, dy);
5288     DrawAllPlayers();
5289
5290     /* scroll in two steps of half tile size to make things smoother */
5291     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5292
5293     /* scroll second step to align at full tile size */
5294     BlitScreenToBitmap(window);
5295   }
5296
5297   DrawAllPlayers();
5298   BackToFront();
5299
5300   SetVideoFrameDelay(frame_delay_value_old);
5301 }
5302
5303 void RelocatePlayer(int jx, int jy, int el_player_raw)
5304 {
5305   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5306   int player_nr = GET_PLAYER_NR(el_player);
5307   struct PlayerInfo *player = &stored_player[player_nr];
5308   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5309   boolean no_delay = (tape.warp_forward);
5310   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5311   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5312   int old_jx = player->jx;
5313   int old_jy = player->jy;
5314   int old_element = Feld[old_jx][old_jy];
5315   int element = Feld[jx][jy];
5316   boolean player_relocated = (old_jx != jx || old_jy != jy);
5317
5318   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5319   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5320   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5321   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5322   int leave_side_horiz = move_dir_horiz;
5323   int leave_side_vert  = move_dir_vert;
5324   int enter_side = enter_side_horiz | enter_side_vert;
5325   int leave_side = leave_side_horiz | leave_side_vert;
5326
5327   if (player->GameOver)         /* do not reanimate dead player */
5328     return;
5329
5330   if (!player_relocated)        /* no need to relocate the player */
5331     return;
5332
5333   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5334   {
5335     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5336     DrawLevelField(jx, jy);
5337   }
5338
5339   if (player->present)
5340   {
5341     while (player->MovPos)
5342     {
5343       ScrollPlayer(player, SCROLL_GO_ON);
5344       ScrollScreen(NULL, SCROLL_GO_ON);
5345
5346       AdvanceFrameAndPlayerCounters(player->index_nr);
5347
5348       DrawPlayer(player);
5349
5350       BackToFront_WithFrameDelay(wait_delay_value);
5351     }
5352
5353     DrawPlayer(player);         /* needed here only to cleanup last field */
5354     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5355
5356     player->is_moving = FALSE;
5357   }
5358
5359   if (IS_CUSTOM_ELEMENT(old_element))
5360     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5361                                CE_LEFT_BY_PLAYER,
5362                                player->index_bit, leave_side);
5363
5364   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5365                                       CE_PLAYER_LEAVES_X,
5366                                       player->index_bit, leave_side);
5367
5368   Feld[jx][jy] = el_player;
5369   InitPlayerField(jx, jy, el_player, TRUE);
5370
5371   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5372      possible that the relocation target field did not contain a player element,
5373      but a walkable element, to which the new player was relocated -- in this
5374      case, restore that (already initialized!) element on the player field */
5375   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5376   {
5377     Feld[jx][jy] = element;     /* restore previously existing element */
5378   }
5379
5380   /* only visually relocate centered player */
5381   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5382                      FALSE, level.instant_relocation);
5383
5384   TestIfPlayerTouchesBadThing(jx, jy);
5385   TestIfPlayerTouchesCustomElement(jx, jy);
5386
5387   if (IS_CUSTOM_ELEMENT(element))
5388     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5389                                player->index_bit, enter_side);
5390
5391   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5392                                       player->index_bit, enter_side);
5393
5394   if (player->is_switching)
5395   {
5396     /* ensure that relocation while still switching an element does not cause
5397        a new element to be treated as also switched directly after relocation
5398        (this is important for teleporter switches that teleport the player to
5399        a place where another teleporter switch is in the same direction, which
5400        would then incorrectly be treated as immediately switched before the
5401        direction key that caused the switch was released) */
5402
5403     player->switch_x += jx - old_jx;
5404     player->switch_y += jy - old_jy;
5405   }
5406 }
5407
5408 void Explode(int ex, int ey, int phase, int mode)
5409 {
5410   int x, y;
5411   int last_phase;
5412   int border_element;
5413
5414   /* !!! eliminate this variable !!! */
5415   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5416
5417   if (game.explosions_delayed)
5418   {
5419     ExplodeField[ex][ey] = mode;
5420     return;
5421   }
5422
5423   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5424   {
5425     int center_element = Feld[ex][ey];
5426     int artwork_element, explosion_element;     /* set these values later */
5427
5428     /* remove things displayed in background while burning dynamite */
5429     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5430       Back[ex][ey] = 0;
5431
5432     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5433     {
5434       /* put moving element to center field (and let it explode there) */
5435       center_element = MovingOrBlocked2Element(ex, ey);
5436       RemoveMovingField(ex, ey);
5437       Feld[ex][ey] = center_element;
5438     }
5439
5440     /* now "center_element" is finally determined -- set related values now */
5441     artwork_element = center_element;           /* for custom player artwork */
5442     explosion_element = center_element;         /* for custom player artwork */
5443
5444     if (IS_PLAYER(ex, ey))
5445     {
5446       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5447
5448       artwork_element = stored_player[player_nr].artwork_element;
5449
5450       if (level.use_explosion_element[player_nr])
5451       {
5452         explosion_element = level.explosion_element[player_nr];
5453         artwork_element = explosion_element;
5454       }
5455     }
5456
5457     if (mode == EX_TYPE_NORMAL ||
5458         mode == EX_TYPE_CENTER ||
5459         mode == EX_TYPE_CROSS)
5460       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5461
5462     last_phase = element_info[explosion_element].explosion_delay + 1;
5463
5464     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5465     {
5466       int xx = x - ex + 1;
5467       int yy = y - ey + 1;
5468       int element;
5469
5470       if (!IN_LEV_FIELD(x, y) ||
5471           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5472           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5473         continue;
5474
5475       element = Feld[x][y];
5476
5477       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5478       {
5479         element = MovingOrBlocked2Element(x, y);
5480
5481         if (!IS_EXPLOSION_PROOF(element))
5482           RemoveMovingField(x, y);
5483       }
5484
5485       /* indestructible elements can only explode in center (but not flames) */
5486       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5487                                            mode == EX_TYPE_BORDER)) ||
5488           element == EL_FLAMES)
5489         continue;
5490
5491       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5492          behaviour, for example when touching a yamyam that explodes to rocks
5493          with active deadly shield, a rock is created under the player !!! */
5494       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5495 #if 0
5496       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5497           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5498            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5499 #else
5500       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5501 #endif
5502       {
5503         if (IS_ACTIVE_BOMB(element))
5504         {
5505           /* re-activate things under the bomb like gate or penguin */
5506           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5507           Back[x][y] = 0;
5508         }
5509
5510         continue;
5511       }
5512
5513       /* save walkable background elements while explosion on same tile */
5514       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5515           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5516         Back[x][y] = element;
5517
5518       /* ignite explodable elements reached by other explosion */
5519       if (element == EL_EXPLOSION)
5520         element = Store2[x][y];
5521
5522       if (AmoebaNr[x][y] &&
5523           (element == EL_AMOEBA_FULL ||
5524            element == EL_BD_AMOEBA ||
5525            element == EL_AMOEBA_GROWING))
5526       {
5527         AmoebaCnt[AmoebaNr[x][y]]--;
5528         AmoebaCnt2[AmoebaNr[x][y]]--;
5529       }
5530
5531       RemoveField(x, y);
5532
5533       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5534       {
5535         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5536
5537         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5538
5539         if (PLAYERINFO(ex, ey)->use_murphy)
5540           Store[x][y] = EL_EMPTY;
5541       }
5542
5543       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5544          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5545       else if (ELEM_IS_PLAYER(center_element))
5546         Store[x][y] = EL_EMPTY;
5547       else if (center_element == EL_YAMYAM)
5548         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5549       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5550         Store[x][y] = element_info[center_element].content.e[xx][yy];
5551 #if 1
5552       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5553          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5554          otherwise) -- FIX THIS !!! */
5555       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5556         Store[x][y] = element_info[element].content.e[1][1];
5557 #else
5558       else if (!CAN_EXPLODE(element))
5559         Store[x][y] = element_info[element].content.e[1][1];
5560 #endif
5561       else
5562         Store[x][y] = EL_EMPTY;
5563
5564       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5565           center_element == EL_AMOEBA_TO_DIAMOND)
5566         Store2[x][y] = element;
5567
5568       Feld[x][y] = EL_EXPLOSION;
5569       GfxElement[x][y] = artwork_element;
5570
5571       ExplodePhase[x][y] = 1;
5572       ExplodeDelay[x][y] = last_phase;
5573
5574       Stop[x][y] = TRUE;
5575     }
5576
5577     if (center_element == EL_YAMYAM)
5578       game.yamyam_content_nr =
5579         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5580
5581     return;
5582   }
5583
5584   if (Stop[ex][ey])
5585     return;
5586
5587   x = ex;
5588   y = ey;
5589
5590   if (phase == 1)
5591     GfxFrame[x][y] = 0;         /* restart explosion animation */
5592
5593   last_phase = ExplodeDelay[x][y];
5594
5595   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5596
5597   /* this can happen if the player leaves an explosion just in time */
5598   if (GfxElement[x][y] == EL_UNDEFINED)
5599     GfxElement[x][y] = EL_EMPTY;
5600
5601   border_element = Store2[x][y];
5602   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5603     border_element = StorePlayer[x][y];
5604
5605   if (phase == element_info[border_element].ignition_delay ||
5606       phase == last_phase)
5607   {
5608     boolean border_explosion = FALSE;
5609
5610     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5611         !PLAYER_EXPLOSION_PROTECTED(x, y))
5612     {
5613       KillPlayerUnlessExplosionProtected(x, y);
5614       border_explosion = TRUE;
5615     }
5616     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5617     {
5618       Feld[x][y] = Store2[x][y];
5619       Store2[x][y] = 0;
5620       Bang(x, y);
5621       border_explosion = TRUE;
5622     }
5623     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5624     {
5625       AmoebeUmwandeln(x, y);
5626       Store2[x][y] = 0;
5627       border_explosion = TRUE;
5628     }
5629
5630     /* if an element just explodes due to another explosion (chain-reaction),
5631        do not immediately end the new explosion when it was the last frame of
5632        the explosion (as it would be done in the following "if"-statement!) */
5633     if (border_explosion && phase == last_phase)
5634       return;
5635   }
5636
5637   if (phase == last_phase)
5638   {
5639     int element;
5640
5641     element = Feld[x][y] = Store[x][y];
5642     Store[x][y] = Store2[x][y] = 0;
5643     GfxElement[x][y] = EL_UNDEFINED;
5644
5645     /* player can escape from explosions and might therefore be still alive */
5646     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5647         element <= EL_PLAYER_IS_EXPLODING_4)
5648     {
5649       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5650       int explosion_element = EL_PLAYER_1 + player_nr;
5651       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5652       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5653
5654       if (level.use_explosion_element[player_nr])
5655         explosion_element = level.explosion_element[player_nr];
5656
5657       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5658                     element_info[explosion_element].content.e[xx][yy]);
5659     }
5660
5661     /* restore probably existing indestructible background element */
5662     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5663       element = Feld[x][y] = Back[x][y];
5664     Back[x][y] = 0;
5665
5666     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5667     GfxDir[x][y] = MV_NONE;
5668     ChangeDelay[x][y] = 0;
5669     ChangePage[x][y] = -1;
5670
5671     CustomValue[x][y] = 0;
5672
5673     InitField_WithBug2(x, y, FALSE);
5674
5675     TEST_DrawLevelField(x, y);
5676
5677     TestIfElementTouchesCustomElement(x, y);
5678
5679     if (GFX_CRUMBLED(element))
5680       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5681
5682     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5683       StorePlayer[x][y] = 0;
5684
5685     if (ELEM_IS_PLAYER(element))
5686       RelocatePlayer(x, y, element);
5687   }
5688   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5689   {
5690     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5691     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5692
5693     if (phase == delay)
5694       TEST_DrawLevelFieldCrumbled(x, y);
5695
5696     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5697     {
5698       DrawLevelElement(x, y, Back[x][y]);
5699       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5700     }
5701     else if (IS_WALKABLE_UNDER(Back[x][y]))
5702     {
5703       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5704       DrawLevelElementThruMask(x, y, Back[x][y]);
5705     }
5706     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5707       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5708   }
5709 }
5710
5711 void DynaExplode(int ex, int ey)
5712 {
5713   int i, j;
5714   int dynabomb_element = Feld[ex][ey];
5715   int dynabomb_size = 1;
5716   boolean dynabomb_xl = FALSE;
5717   struct PlayerInfo *player;
5718   static int xy[4][2] =
5719   {
5720     { 0, -1 },
5721     { -1, 0 },
5722     { +1, 0 },
5723     { 0, +1 }
5724   };
5725
5726   if (IS_ACTIVE_BOMB(dynabomb_element))
5727   {
5728     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5729     dynabomb_size = player->dynabomb_size;
5730     dynabomb_xl = player->dynabomb_xl;
5731     player->dynabombs_left++;
5732   }
5733
5734   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5735
5736   for (i = 0; i < NUM_DIRECTIONS; i++)
5737   {
5738     for (j = 1; j <= dynabomb_size; j++)
5739     {
5740       int x = ex + j * xy[i][0];
5741       int y = ey + j * xy[i][1];
5742       int element;
5743
5744       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5745         break;
5746
5747       element = Feld[x][y];
5748
5749       /* do not restart explosions of fields with active bombs */
5750       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5751         continue;
5752
5753       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5754
5755       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5756           !IS_DIGGABLE(element) && !dynabomb_xl)
5757         break;
5758     }
5759   }
5760 }
5761
5762 void Bang(int x, int y)
5763 {
5764   int element = MovingOrBlocked2Element(x, y);
5765   int explosion_type = EX_TYPE_NORMAL;
5766
5767   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5768   {
5769     struct PlayerInfo *player = PLAYERINFO(x, y);
5770
5771     element = Feld[x][y] = player->initial_element;
5772
5773     if (level.use_explosion_element[player->index_nr])
5774     {
5775       int explosion_element = level.explosion_element[player->index_nr];
5776
5777       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5778         explosion_type = EX_TYPE_CROSS;
5779       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5780         explosion_type = EX_TYPE_CENTER;
5781     }
5782   }
5783
5784   switch (element)
5785   {
5786     case EL_BUG:
5787     case EL_SPACESHIP:
5788     case EL_BD_BUTTERFLY:
5789     case EL_BD_FIREFLY:
5790     case EL_YAMYAM:
5791     case EL_DARK_YAMYAM:
5792     case EL_ROBOT:
5793     case EL_PACMAN:
5794     case EL_MOLE:
5795       RaiseScoreElement(element);
5796       break;
5797
5798     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5799     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5800     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5801     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5802     case EL_DYNABOMB_INCREASE_NUMBER:
5803     case EL_DYNABOMB_INCREASE_SIZE:
5804     case EL_DYNABOMB_INCREASE_POWER:
5805       explosion_type = EX_TYPE_DYNA;
5806       break;
5807
5808     case EL_DC_LANDMINE:
5809       explosion_type = EX_TYPE_CENTER;
5810       break;
5811
5812     case EL_PENGUIN:
5813     case EL_LAMP:
5814     case EL_LAMP_ACTIVE:
5815     case EL_AMOEBA_TO_DIAMOND:
5816       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5817         explosion_type = EX_TYPE_CENTER;
5818       break;
5819
5820     default:
5821       if (element_info[element].explosion_type == EXPLODES_CROSS)
5822         explosion_type = EX_TYPE_CROSS;
5823       else if (element_info[element].explosion_type == EXPLODES_1X1)
5824         explosion_type = EX_TYPE_CENTER;
5825       break;
5826   }
5827
5828   if (explosion_type == EX_TYPE_DYNA)
5829     DynaExplode(x, y);
5830   else
5831     Explode(x, y, EX_PHASE_START, explosion_type);
5832
5833   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5834 }
5835
5836 void SplashAcid(int x, int y)
5837 {
5838   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5839       (!IN_LEV_FIELD(x - 1, y - 2) ||
5840        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5841     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5842
5843   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5844       (!IN_LEV_FIELD(x + 1, y - 2) ||
5845        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5846     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5847
5848   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5849 }
5850
5851 static void InitBeltMovement()
5852 {
5853   static int belt_base_element[4] =
5854   {
5855     EL_CONVEYOR_BELT_1_LEFT,
5856     EL_CONVEYOR_BELT_2_LEFT,
5857     EL_CONVEYOR_BELT_3_LEFT,
5858     EL_CONVEYOR_BELT_4_LEFT
5859   };
5860   static int belt_base_active_element[4] =
5861   {
5862     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5863     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5864     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5865     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5866   };
5867
5868   int x, y, i, j;
5869
5870   /* set frame order for belt animation graphic according to belt direction */
5871   for (i = 0; i < NUM_BELTS; i++)
5872   {
5873     int belt_nr = i;
5874
5875     for (j = 0; j < NUM_BELT_PARTS; j++)
5876     {
5877       int element = belt_base_active_element[belt_nr] + j;
5878       int graphic_1 = el2img(element);
5879       int graphic_2 = el2panelimg(element);
5880
5881       if (game.belt_dir[i] == MV_LEFT)
5882       {
5883         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5884         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5885       }
5886       else
5887       {
5888         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5889         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5890       }
5891     }
5892   }
5893
5894   SCAN_PLAYFIELD(x, y)
5895   {
5896     int element = Feld[x][y];
5897
5898     for (i = 0; i < NUM_BELTS; i++)
5899     {
5900       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5901       {
5902         int e_belt_nr = getBeltNrFromBeltElement(element);
5903         int belt_nr = i;
5904
5905         if (e_belt_nr == belt_nr)
5906         {
5907           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5908
5909           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5910         }
5911       }
5912     }
5913   }
5914 }
5915
5916 static void ToggleBeltSwitch(int x, int y)
5917 {
5918   static int belt_base_element[4] =
5919   {
5920     EL_CONVEYOR_BELT_1_LEFT,
5921     EL_CONVEYOR_BELT_2_LEFT,
5922     EL_CONVEYOR_BELT_3_LEFT,
5923     EL_CONVEYOR_BELT_4_LEFT
5924   };
5925   static int belt_base_active_element[4] =
5926   {
5927     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5928     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5929     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5930     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5931   };
5932   static int belt_base_switch_element[4] =
5933   {
5934     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5935     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5936     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5937     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5938   };
5939   static int belt_move_dir[4] =
5940   {
5941     MV_LEFT,
5942     MV_NONE,
5943     MV_RIGHT,
5944     MV_NONE,
5945   };
5946
5947   int element = Feld[x][y];
5948   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5949   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5950   int belt_dir = belt_move_dir[belt_dir_nr];
5951   int xx, yy, i;
5952
5953   if (!IS_BELT_SWITCH(element))
5954     return;
5955
5956   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5957   game.belt_dir[belt_nr] = belt_dir;
5958
5959   if (belt_dir_nr == 3)
5960     belt_dir_nr = 1;
5961
5962   /* set frame order for belt animation graphic according to belt direction */
5963   for (i = 0; i < NUM_BELT_PARTS; i++)
5964   {
5965     int element = belt_base_active_element[belt_nr] + i;
5966     int graphic_1 = el2img(element);
5967     int graphic_2 = el2panelimg(element);
5968
5969     if (belt_dir == MV_LEFT)
5970     {
5971       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5972       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5973     }
5974     else
5975     {
5976       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5977       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5978     }
5979   }
5980
5981   SCAN_PLAYFIELD(xx, yy)
5982   {
5983     int element = Feld[xx][yy];
5984
5985     if (IS_BELT_SWITCH(element))
5986     {
5987       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5988
5989       if (e_belt_nr == belt_nr)
5990       {
5991         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5992         TEST_DrawLevelField(xx, yy);
5993       }
5994     }
5995     else if (IS_BELT(element) && belt_dir != MV_NONE)
5996     {
5997       int e_belt_nr = getBeltNrFromBeltElement(element);
5998
5999       if (e_belt_nr == belt_nr)
6000       {
6001         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6002
6003         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6004         TEST_DrawLevelField(xx, yy);
6005       }
6006     }
6007     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6008     {
6009       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6010
6011       if (e_belt_nr == belt_nr)
6012       {
6013         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6014
6015         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6016         TEST_DrawLevelField(xx, yy);
6017       }
6018     }
6019   }
6020 }
6021
6022 static void ToggleSwitchgateSwitch(int x, int y)
6023 {
6024   int xx, yy;
6025
6026   game.switchgate_pos = !game.switchgate_pos;
6027
6028   SCAN_PLAYFIELD(xx, yy)
6029   {
6030     int element = Feld[xx][yy];
6031
6032     if (element == EL_SWITCHGATE_SWITCH_UP)
6033     {
6034       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6035       TEST_DrawLevelField(xx, yy);
6036     }
6037     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6038     {
6039       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6040       TEST_DrawLevelField(xx, yy);
6041     }
6042     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6043     {
6044       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6045       TEST_DrawLevelField(xx, yy);
6046     }
6047     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6048     {
6049       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6050       TEST_DrawLevelField(xx, yy);
6051     }
6052     else if (element == EL_SWITCHGATE_OPEN ||
6053              element == EL_SWITCHGATE_OPENING)
6054     {
6055       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6056
6057       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6058     }
6059     else if (element == EL_SWITCHGATE_CLOSED ||
6060              element == EL_SWITCHGATE_CLOSING)
6061     {
6062       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6063
6064       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6065     }
6066   }
6067 }
6068
6069 static int getInvisibleActiveFromInvisibleElement(int element)
6070 {
6071   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6072           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6073           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6074           element);
6075 }
6076
6077 static int getInvisibleFromInvisibleActiveElement(int element)
6078 {
6079   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6080           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6081           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6082           element);
6083 }
6084
6085 static void RedrawAllLightSwitchesAndInvisibleElements()
6086 {
6087   int x, y;
6088
6089   SCAN_PLAYFIELD(x, y)
6090   {
6091     int element = Feld[x][y];
6092
6093     if (element == EL_LIGHT_SWITCH &&
6094         game.light_time_left > 0)
6095     {
6096       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6097       TEST_DrawLevelField(x, y);
6098     }
6099     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6100              game.light_time_left == 0)
6101     {
6102       Feld[x][y] = EL_LIGHT_SWITCH;
6103       TEST_DrawLevelField(x, y);
6104     }
6105     else if (element == EL_EMC_DRIPPER &&
6106              game.light_time_left > 0)
6107     {
6108       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6109       TEST_DrawLevelField(x, y);
6110     }
6111     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6112              game.light_time_left == 0)
6113     {
6114       Feld[x][y] = EL_EMC_DRIPPER;
6115       TEST_DrawLevelField(x, y);
6116     }
6117     else if (element == EL_INVISIBLE_STEELWALL ||
6118              element == EL_INVISIBLE_WALL ||
6119              element == EL_INVISIBLE_SAND)
6120     {
6121       if (game.light_time_left > 0)
6122         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6123
6124       TEST_DrawLevelField(x, y);
6125
6126       /* uncrumble neighbour fields, if needed */
6127       if (element == EL_INVISIBLE_SAND)
6128         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6129     }
6130     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6131              element == EL_INVISIBLE_WALL_ACTIVE ||
6132              element == EL_INVISIBLE_SAND_ACTIVE)
6133     {
6134       if (game.light_time_left == 0)
6135         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6136
6137       TEST_DrawLevelField(x, y);
6138
6139       /* re-crumble neighbour fields, if needed */
6140       if (element == EL_INVISIBLE_SAND)
6141         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6142     }
6143   }
6144 }
6145
6146 static void RedrawAllInvisibleElementsForLenses()
6147 {
6148   int x, y;
6149
6150   SCAN_PLAYFIELD(x, y)
6151   {
6152     int element = Feld[x][y];
6153
6154     if (element == EL_EMC_DRIPPER &&
6155         game.lenses_time_left > 0)
6156     {
6157       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6158       TEST_DrawLevelField(x, y);
6159     }
6160     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6161              game.lenses_time_left == 0)
6162     {
6163       Feld[x][y] = EL_EMC_DRIPPER;
6164       TEST_DrawLevelField(x, y);
6165     }
6166     else if (element == EL_INVISIBLE_STEELWALL ||
6167              element == EL_INVISIBLE_WALL ||
6168              element == EL_INVISIBLE_SAND)
6169     {
6170       if (game.lenses_time_left > 0)
6171         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6172
6173       TEST_DrawLevelField(x, y);
6174
6175       /* uncrumble neighbour fields, if needed */
6176       if (element == EL_INVISIBLE_SAND)
6177         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6178     }
6179     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6180              element == EL_INVISIBLE_WALL_ACTIVE ||
6181              element == EL_INVISIBLE_SAND_ACTIVE)
6182     {
6183       if (game.lenses_time_left == 0)
6184         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6185
6186       TEST_DrawLevelField(x, y);
6187
6188       /* re-crumble neighbour fields, if needed */
6189       if (element == EL_INVISIBLE_SAND)
6190         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6191     }
6192   }
6193 }
6194
6195 static void RedrawAllInvisibleElementsForMagnifier()
6196 {
6197   int x, y;
6198
6199   SCAN_PLAYFIELD(x, y)
6200   {
6201     int element = Feld[x][y];
6202
6203     if (element == EL_EMC_FAKE_GRASS &&
6204         game.magnify_time_left > 0)
6205     {
6206       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6207       TEST_DrawLevelField(x, y);
6208     }
6209     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6210              game.magnify_time_left == 0)
6211     {
6212       Feld[x][y] = EL_EMC_FAKE_GRASS;
6213       TEST_DrawLevelField(x, y);
6214     }
6215     else if (IS_GATE_GRAY(element) &&
6216              game.magnify_time_left > 0)
6217     {
6218       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6219                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6220                     IS_EM_GATE_GRAY(element) ?
6221                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6222                     IS_EMC_GATE_GRAY(element) ?
6223                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6224                     IS_DC_GATE_GRAY(element) ?
6225                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6226                     element);
6227       TEST_DrawLevelField(x, y);
6228     }
6229     else if (IS_GATE_GRAY_ACTIVE(element) &&
6230              game.magnify_time_left == 0)
6231     {
6232       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6233                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6234                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6235                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6236                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6237                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6238                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6239                     EL_DC_GATE_WHITE_GRAY :
6240                     element);
6241       TEST_DrawLevelField(x, y);
6242     }
6243   }
6244 }
6245
6246 static void ToggleLightSwitch(int x, int y)
6247 {
6248   int element = Feld[x][y];
6249
6250   game.light_time_left =
6251     (element == EL_LIGHT_SWITCH ?
6252      level.time_light * FRAMES_PER_SECOND : 0);
6253
6254   RedrawAllLightSwitchesAndInvisibleElements();
6255 }
6256
6257 static void ActivateTimegateSwitch(int x, int y)
6258 {
6259   int xx, yy;
6260
6261   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6262
6263   SCAN_PLAYFIELD(xx, yy)
6264   {
6265     int element = Feld[xx][yy];
6266
6267     if (element == EL_TIMEGATE_CLOSED ||
6268         element == EL_TIMEGATE_CLOSING)
6269     {
6270       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6271       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6272     }
6273
6274     /*
6275     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6276     {
6277       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6278       TEST_DrawLevelField(xx, yy);
6279     }
6280     */
6281
6282   }
6283
6284   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6285                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6286 }
6287
6288 void Impact(int x, int y)
6289 {
6290   boolean last_line = (y == lev_fieldy - 1);
6291   boolean object_hit = FALSE;
6292   boolean impact = (last_line || object_hit);
6293   int element = Feld[x][y];
6294   int smashed = EL_STEELWALL;
6295
6296   if (!last_line)       /* check if element below was hit */
6297   {
6298     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6299       return;
6300
6301     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6302                                          MovDir[x][y + 1] != MV_DOWN ||
6303                                          MovPos[x][y + 1] <= TILEY / 2));
6304
6305     /* do not smash moving elements that left the smashed field in time */
6306     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6307         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6308       object_hit = FALSE;
6309
6310 #if USE_QUICKSAND_IMPACT_BUGFIX
6311     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6312     {
6313       RemoveMovingField(x, y + 1);
6314       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6315       Feld[x][y + 2] = EL_ROCK;
6316       TEST_DrawLevelField(x, y + 2);
6317
6318       object_hit = TRUE;
6319     }
6320
6321     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6322     {
6323       RemoveMovingField(x, y + 1);
6324       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6325       Feld[x][y + 2] = EL_ROCK;
6326       TEST_DrawLevelField(x, y + 2);
6327
6328       object_hit = TRUE;
6329     }
6330 #endif
6331
6332     if (object_hit)
6333       smashed = MovingOrBlocked2Element(x, y + 1);
6334
6335     impact = (last_line || object_hit);
6336   }
6337
6338   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6339   {
6340     SplashAcid(x, y + 1);
6341     return;
6342   }
6343
6344   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6345   /* only reset graphic animation if graphic really changes after impact */
6346   if (impact &&
6347       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6348   {
6349     ResetGfxAnimation(x, y);
6350     TEST_DrawLevelField(x, y);
6351   }
6352
6353   if (impact && CAN_EXPLODE_IMPACT(element))
6354   {
6355     Bang(x, y);
6356     return;
6357   }
6358   else if (impact && element == EL_PEARL &&
6359            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6360   {
6361     ResetGfxAnimation(x, y);
6362
6363     Feld[x][y] = EL_PEARL_BREAKING;
6364     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6365     return;
6366   }
6367   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6368   {
6369     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6370
6371     return;
6372   }
6373
6374   if (impact && element == EL_AMOEBA_DROP)
6375   {
6376     if (object_hit && IS_PLAYER(x, y + 1))
6377       KillPlayerUnlessEnemyProtected(x, y + 1);
6378     else if (object_hit && smashed == EL_PENGUIN)
6379       Bang(x, y + 1);
6380     else
6381     {
6382       Feld[x][y] = EL_AMOEBA_GROWING;
6383       Store[x][y] = EL_AMOEBA_WET;
6384
6385       ResetRandomAnimationValue(x, y);
6386     }
6387     return;
6388   }
6389
6390   if (object_hit)               /* check which object was hit */
6391   {
6392     if ((CAN_PASS_MAGIC_WALL(element) && 
6393          (smashed == EL_MAGIC_WALL ||
6394           smashed == EL_BD_MAGIC_WALL)) ||
6395         (CAN_PASS_DC_MAGIC_WALL(element) &&
6396          smashed == EL_DC_MAGIC_WALL))
6397     {
6398       int xx, yy;
6399       int activated_magic_wall =
6400         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6401          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6402          EL_DC_MAGIC_WALL_ACTIVE);
6403
6404       /* activate magic wall / mill */
6405       SCAN_PLAYFIELD(xx, yy)
6406       {
6407         if (Feld[xx][yy] == smashed)
6408           Feld[xx][yy] = activated_magic_wall;
6409       }
6410
6411       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6412       game.magic_wall_active = TRUE;
6413
6414       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6415                             SND_MAGIC_WALL_ACTIVATING :
6416                             smashed == EL_BD_MAGIC_WALL ?
6417                             SND_BD_MAGIC_WALL_ACTIVATING :
6418                             SND_DC_MAGIC_WALL_ACTIVATING));
6419     }
6420
6421     if (IS_PLAYER(x, y + 1))
6422     {
6423       if (CAN_SMASH_PLAYER(element))
6424       {
6425         KillPlayerUnlessEnemyProtected(x, y + 1);
6426         return;
6427       }
6428     }
6429     else if (smashed == EL_PENGUIN)
6430     {
6431       if (CAN_SMASH_PLAYER(element))
6432       {
6433         Bang(x, y + 1);
6434         return;
6435       }
6436     }
6437     else if (element == EL_BD_DIAMOND)
6438     {
6439       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6440       {
6441         Bang(x, y + 1);
6442         return;
6443       }
6444     }
6445     else if (((element == EL_SP_INFOTRON ||
6446                element == EL_SP_ZONK) &&
6447               (smashed == EL_SP_SNIKSNAK ||
6448                smashed == EL_SP_ELECTRON ||
6449                smashed == EL_SP_DISK_ORANGE)) ||
6450              (element == EL_SP_INFOTRON &&
6451               smashed == EL_SP_DISK_YELLOW))
6452     {
6453       Bang(x, y + 1);
6454       return;
6455     }
6456     else if (CAN_SMASH_EVERYTHING(element))
6457     {
6458       if (IS_CLASSIC_ENEMY(smashed) ||
6459           CAN_EXPLODE_SMASHED(smashed))
6460       {
6461         Bang(x, y + 1);
6462         return;
6463       }
6464       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6465       {
6466         if (smashed == EL_LAMP ||
6467             smashed == EL_LAMP_ACTIVE)
6468         {
6469           Bang(x, y + 1);
6470           return;
6471         }
6472         else if (smashed == EL_NUT)
6473         {
6474           Feld[x][y + 1] = EL_NUT_BREAKING;
6475           PlayLevelSound(x, y, SND_NUT_BREAKING);
6476           RaiseScoreElement(EL_NUT);
6477           return;
6478         }
6479         else if (smashed == EL_PEARL)
6480         {
6481           ResetGfxAnimation(x, y);
6482
6483           Feld[x][y + 1] = EL_PEARL_BREAKING;
6484           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6485           return;
6486         }
6487         else if (smashed == EL_DIAMOND)
6488         {
6489           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6490           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6491           return;
6492         }
6493         else if (IS_BELT_SWITCH(smashed))
6494         {
6495           ToggleBeltSwitch(x, y + 1);
6496         }
6497         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6498                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6499                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6500                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6501         {
6502           ToggleSwitchgateSwitch(x, y + 1);
6503         }
6504         else if (smashed == EL_LIGHT_SWITCH ||
6505                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6506         {
6507           ToggleLightSwitch(x, y + 1);
6508         }
6509         else
6510         {
6511           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6512
6513           CheckElementChangeBySide(x, y + 1, smashed, element,
6514                                    CE_SWITCHED, CH_SIDE_TOP);
6515           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6516                                             CH_SIDE_TOP);
6517         }
6518       }
6519       else
6520       {
6521         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6522       }
6523     }
6524   }
6525
6526   /* play sound of magic wall / mill */
6527   if (!last_line &&
6528       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6529        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6530        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6531   {
6532     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6533       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6534     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6535       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6536     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6537       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6538
6539     return;
6540   }
6541
6542   /* play sound of object that hits the ground */
6543   if (last_line || object_hit)
6544     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6545 }
6546
6547 inline static void TurnRoundExt(int x, int y)
6548 {
6549   static struct
6550   {
6551     int dx, dy;
6552   } move_xy[] =
6553   {
6554     {  0,  0 },
6555     { -1,  0 },
6556     { +1,  0 },
6557     {  0,  0 },
6558     {  0, -1 },
6559     {  0,  0 }, { 0, 0 }, { 0, 0 },
6560     {  0, +1 }
6561   };
6562   static struct
6563   {
6564     int left, right, back;
6565   } turn[] =
6566   {
6567     { 0,        0,              0        },
6568     { MV_DOWN,  MV_UP,          MV_RIGHT },
6569     { MV_UP,    MV_DOWN,        MV_LEFT  },
6570     { 0,        0,              0        },
6571     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6572     { 0,        0,              0        },
6573     { 0,        0,              0        },
6574     { 0,        0,              0        },
6575     { MV_RIGHT, MV_LEFT,        MV_UP    }
6576   };
6577
6578   int element = Feld[x][y];
6579   int move_pattern = element_info[element].move_pattern;
6580
6581   int old_move_dir = MovDir[x][y];
6582   int left_dir  = turn[old_move_dir].left;
6583   int right_dir = turn[old_move_dir].right;
6584   int back_dir  = turn[old_move_dir].back;
6585
6586   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6587   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6588   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6589   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6590
6591   int left_x  = x + left_dx,  left_y  = y + left_dy;
6592   int right_x = x + right_dx, right_y = y + right_dy;
6593   int move_x  = x + move_dx,  move_y  = y + move_dy;
6594
6595   int xx, yy;
6596
6597   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6598   {
6599     TestIfBadThingTouchesOtherBadThing(x, y);
6600
6601     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6602       MovDir[x][y] = right_dir;
6603     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6604       MovDir[x][y] = left_dir;
6605
6606     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6607       MovDelay[x][y] = 9;
6608     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6609       MovDelay[x][y] = 1;
6610   }
6611   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6612   {
6613     TestIfBadThingTouchesOtherBadThing(x, y);
6614
6615     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6616       MovDir[x][y] = left_dir;
6617     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6618       MovDir[x][y] = right_dir;
6619
6620     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6621       MovDelay[x][y] = 9;
6622     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6623       MovDelay[x][y] = 1;
6624   }
6625   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6626   {
6627     TestIfBadThingTouchesOtherBadThing(x, y);
6628
6629     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6630       MovDir[x][y] = left_dir;
6631     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6632       MovDir[x][y] = right_dir;
6633
6634     if (MovDir[x][y] != old_move_dir)
6635       MovDelay[x][y] = 9;
6636   }
6637   else if (element == EL_YAMYAM)
6638   {
6639     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6640     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6641
6642     if (can_turn_left && can_turn_right)
6643       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6644     else if (can_turn_left)
6645       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6646     else if (can_turn_right)
6647       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6648     else
6649       MovDir[x][y] = back_dir;
6650
6651     MovDelay[x][y] = 16 + 16 * RND(3);
6652   }
6653   else if (element == EL_DARK_YAMYAM)
6654   {
6655     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6656                                                          left_x, left_y);
6657     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6658                                                          right_x, right_y);
6659
6660     if (can_turn_left && can_turn_right)
6661       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6662     else if (can_turn_left)
6663       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6664     else if (can_turn_right)
6665       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6666     else
6667       MovDir[x][y] = back_dir;
6668
6669     MovDelay[x][y] = 16 + 16 * RND(3);
6670   }
6671   else if (element == EL_PACMAN)
6672   {
6673     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6674     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6675
6676     if (can_turn_left && can_turn_right)
6677       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6678     else if (can_turn_left)
6679       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6680     else if (can_turn_right)
6681       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6682     else
6683       MovDir[x][y] = back_dir;
6684
6685     MovDelay[x][y] = 6 + RND(40);
6686   }
6687   else if (element == EL_PIG)
6688   {
6689     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6690     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6691     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6692     boolean should_turn_left, should_turn_right, should_move_on;
6693     int rnd_value = 24;
6694     int rnd = RND(rnd_value);
6695
6696     should_turn_left = (can_turn_left &&
6697                         (!can_move_on ||
6698                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6699                                                    y + back_dy + left_dy)));
6700     should_turn_right = (can_turn_right &&
6701                          (!can_move_on ||
6702                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6703                                                     y + back_dy + right_dy)));
6704     should_move_on = (can_move_on &&
6705                       (!can_turn_left ||
6706                        !can_turn_right ||
6707                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6708                                                  y + move_dy + left_dy) ||
6709                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6710                                                  y + move_dy + right_dy)));
6711
6712     if (should_turn_left || should_turn_right || should_move_on)
6713     {
6714       if (should_turn_left && should_turn_right && should_move_on)
6715         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6716                         rnd < 2 * rnd_value / 3 ? right_dir :
6717                         old_move_dir);
6718       else if (should_turn_left && should_turn_right)
6719         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6720       else if (should_turn_left && should_move_on)
6721         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6722       else if (should_turn_right && should_move_on)
6723         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6724       else if (should_turn_left)
6725         MovDir[x][y] = left_dir;
6726       else if (should_turn_right)
6727         MovDir[x][y] = right_dir;
6728       else if (should_move_on)
6729         MovDir[x][y] = old_move_dir;
6730     }
6731     else if (can_move_on && rnd > rnd_value / 8)
6732       MovDir[x][y] = old_move_dir;
6733     else if (can_turn_left && can_turn_right)
6734       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6735     else if (can_turn_left && rnd > rnd_value / 8)
6736       MovDir[x][y] = left_dir;
6737     else if (can_turn_right && rnd > rnd_value/8)
6738       MovDir[x][y] = right_dir;
6739     else
6740       MovDir[x][y] = back_dir;
6741
6742     xx = x + move_xy[MovDir[x][y]].dx;
6743     yy = y + move_xy[MovDir[x][y]].dy;
6744
6745     if (!IN_LEV_FIELD(xx, yy) ||
6746         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6747       MovDir[x][y] = old_move_dir;
6748
6749     MovDelay[x][y] = 0;
6750   }
6751   else if (element == EL_DRAGON)
6752   {
6753     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6754     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6755     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6756     int rnd_value = 24;
6757     int rnd = RND(rnd_value);
6758
6759     if (can_move_on && rnd > rnd_value / 8)
6760       MovDir[x][y] = old_move_dir;
6761     else if (can_turn_left && can_turn_right)
6762       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6763     else if (can_turn_left && rnd > rnd_value / 8)
6764       MovDir[x][y] = left_dir;
6765     else if (can_turn_right && rnd > rnd_value / 8)
6766       MovDir[x][y] = right_dir;
6767     else
6768       MovDir[x][y] = back_dir;
6769
6770     xx = x + move_xy[MovDir[x][y]].dx;
6771     yy = y + move_xy[MovDir[x][y]].dy;
6772
6773     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6774       MovDir[x][y] = old_move_dir;
6775
6776     MovDelay[x][y] = 0;
6777   }
6778   else if (element == EL_MOLE)
6779   {
6780     boolean can_move_on =
6781       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6782                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6783                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6784     if (!can_move_on)
6785     {
6786       boolean can_turn_left =
6787         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6788                               IS_AMOEBOID(Feld[left_x][left_y])));
6789
6790       boolean can_turn_right =
6791         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6792                               IS_AMOEBOID(Feld[right_x][right_y])));
6793
6794       if (can_turn_left && can_turn_right)
6795         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6796       else if (can_turn_left)
6797         MovDir[x][y] = left_dir;
6798       else
6799         MovDir[x][y] = right_dir;
6800     }
6801
6802     if (MovDir[x][y] != old_move_dir)
6803       MovDelay[x][y] = 9;
6804   }
6805   else if (element == EL_BALLOON)
6806   {
6807     MovDir[x][y] = game.wind_direction;
6808     MovDelay[x][y] = 0;
6809   }
6810   else if (element == EL_SPRING)
6811   {
6812     if (MovDir[x][y] & MV_HORIZONTAL)
6813     {
6814       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6815           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6816       {
6817         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6818         ResetGfxAnimation(move_x, move_y);
6819         TEST_DrawLevelField(move_x, move_y);
6820
6821         MovDir[x][y] = back_dir;
6822       }
6823       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6824                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6825         MovDir[x][y] = MV_NONE;
6826     }
6827
6828     MovDelay[x][y] = 0;
6829   }
6830   else if (element == EL_ROBOT ||
6831            element == EL_SATELLITE ||
6832            element == EL_PENGUIN ||
6833            element == EL_EMC_ANDROID)
6834   {
6835     int attr_x = -1, attr_y = -1;
6836
6837     if (AllPlayersGone)
6838     {
6839       attr_x = ExitX;
6840       attr_y = ExitY;
6841     }
6842     else
6843     {
6844       int i;
6845
6846       for (i = 0; i < MAX_PLAYERS; i++)
6847       {
6848         struct PlayerInfo *player = &stored_player[i];
6849         int jx = player->jx, jy = player->jy;
6850
6851         if (!player->active)
6852           continue;
6853
6854         if (attr_x == -1 ||
6855             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6856         {
6857           attr_x = jx;
6858           attr_y = jy;
6859         }
6860       }
6861     }
6862
6863     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6864         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6865          game.engine_version < VERSION_IDENT(3,1,0,0)))
6866     {
6867       attr_x = ZX;
6868       attr_y = ZY;
6869     }
6870
6871     if (element == EL_PENGUIN)
6872     {
6873       int i;
6874       static int xy[4][2] =
6875       {
6876         { 0, -1 },
6877         { -1, 0 },
6878         { +1, 0 },
6879         { 0, +1 }
6880       };
6881
6882       for (i = 0; i < NUM_DIRECTIONS; i++)
6883       {
6884         int ex = x + xy[i][0];
6885         int ey = y + xy[i][1];
6886
6887         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6888                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6889                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6890                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6891         {
6892           attr_x = ex;
6893           attr_y = ey;
6894           break;
6895         }
6896       }
6897     }
6898
6899     MovDir[x][y] = MV_NONE;
6900     if (attr_x < x)
6901       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6902     else if (attr_x > x)
6903       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6904     if (attr_y < y)
6905       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6906     else if (attr_y > y)
6907       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6908
6909     if (element == EL_ROBOT)
6910     {
6911       int newx, newy;
6912
6913       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6914         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6915       Moving2Blocked(x, y, &newx, &newy);
6916
6917       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6918         MovDelay[x][y] = 8 + 8 * !RND(3);
6919       else
6920         MovDelay[x][y] = 16;
6921     }
6922     else if (element == EL_PENGUIN)
6923     {
6924       int newx, newy;
6925
6926       MovDelay[x][y] = 1;
6927
6928       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6929       {
6930         boolean first_horiz = RND(2);
6931         int new_move_dir = MovDir[x][y];
6932
6933         MovDir[x][y] =
6934           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6935         Moving2Blocked(x, y, &newx, &newy);
6936
6937         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6938           return;
6939
6940         MovDir[x][y] =
6941           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6942         Moving2Blocked(x, y, &newx, &newy);
6943
6944         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6945           return;
6946
6947         MovDir[x][y] = old_move_dir;
6948         return;
6949       }
6950     }
6951     else if (element == EL_SATELLITE)
6952     {
6953       int newx, newy;
6954
6955       MovDelay[x][y] = 1;
6956
6957       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6958       {
6959         boolean first_horiz = RND(2);
6960         int new_move_dir = MovDir[x][y];
6961
6962         MovDir[x][y] =
6963           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6964         Moving2Blocked(x, y, &newx, &newy);
6965
6966         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6967           return;
6968
6969         MovDir[x][y] =
6970           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6971         Moving2Blocked(x, y, &newx, &newy);
6972
6973         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6974           return;
6975
6976         MovDir[x][y] = old_move_dir;
6977         return;
6978       }
6979     }
6980     else if (element == EL_EMC_ANDROID)
6981     {
6982       static int check_pos[16] =
6983       {
6984         -1,             /*  0 => (invalid)          */
6985         7,              /*  1 => MV_LEFT            */
6986         3,              /*  2 => MV_RIGHT           */
6987         -1,             /*  3 => (invalid)          */
6988         1,              /*  4 =>            MV_UP   */
6989         0,              /*  5 => MV_LEFT  | MV_UP   */
6990         2,              /*  6 => MV_RIGHT | MV_UP   */
6991         -1,             /*  7 => (invalid)          */
6992         5,              /*  8 =>            MV_DOWN */
6993         6,              /*  9 => MV_LEFT  | MV_DOWN */
6994         4,              /* 10 => MV_RIGHT | MV_DOWN */
6995         -1,             /* 11 => (invalid)          */
6996         -1,             /* 12 => (invalid)          */
6997         -1,             /* 13 => (invalid)          */
6998         -1,             /* 14 => (invalid)          */
6999         -1,             /* 15 => (invalid)          */
7000       };
7001       static struct
7002       {
7003         int dx, dy;
7004         int dir;
7005       } check_xy[8] =
7006       {
7007         { -1, -1,       MV_LEFT  | MV_UP   },
7008         {  0, -1,                  MV_UP   },
7009         { +1, -1,       MV_RIGHT | MV_UP   },
7010         { +1,  0,       MV_RIGHT           },
7011         { +1, +1,       MV_RIGHT | MV_DOWN },
7012         {  0, +1,                  MV_DOWN },
7013         { -1, +1,       MV_LEFT  | MV_DOWN },
7014         { -1,  0,       MV_LEFT            },
7015       };
7016       int start_pos, check_order;
7017       boolean can_clone = FALSE;
7018       int i;
7019
7020       /* check if there is any free field around current position */
7021       for (i = 0; i < 8; i++)
7022       {
7023         int newx = x + check_xy[i].dx;
7024         int newy = y + check_xy[i].dy;
7025
7026         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7027         {
7028           can_clone = TRUE;
7029
7030           break;
7031         }
7032       }
7033
7034       if (can_clone)            /* randomly find an element to clone */
7035       {
7036         can_clone = FALSE;
7037
7038         start_pos = check_pos[RND(8)];
7039         check_order = (RND(2) ? -1 : +1);
7040
7041         for (i = 0; i < 8; i++)
7042         {
7043           int pos_raw = start_pos + i * check_order;
7044           int pos = (pos_raw + 8) % 8;
7045           int newx = x + check_xy[pos].dx;
7046           int newy = y + check_xy[pos].dy;
7047
7048           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7049           {
7050             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7051             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7052
7053             Store[x][y] = Feld[newx][newy];
7054
7055             can_clone = TRUE;
7056
7057             break;
7058           }
7059         }
7060       }
7061
7062       if (can_clone)            /* randomly find a direction to move */
7063       {
7064         can_clone = FALSE;
7065
7066         start_pos = check_pos[RND(8)];
7067         check_order = (RND(2) ? -1 : +1);
7068
7069         for (i = 0; i < 8; i++)
7070         {
7071           int pos_raw = start_pos + i * check_order;
7072           int pos = (pos_raw + 8) % 8;
7073           int newx = x + check_xy[pos].dx;
7074           int newy = y + check_xy[pos].dy;
7075           int new_move_dir = check_xy[pos].dir;
7076
7077           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7078           {
7079             MovDir[x][y] = new_move_dir;
7080             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7081
7082             can_clone = TRUE;
7083
7084             break;
7085           }
7086         }
7087       }
7088
7089       if (can_clone)            /* cloning and moving successful */
7090         return;
7091
7092       /* cannot clone -- try to move towards player */
7093
7094       start_pos = check_pos[MovDir[x][y] & 0x0f];
7095       check_order = (RND(2) ? -1 : +1);
7096
7097       for (i = 0; i < 3; i++)
7098       {
7099         /* first check start_pos, then previous/next or (next/previous) pos */
7100         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7101         int pos = (pos_raw + 8) % 8;
7102         int newx = x + check_xy[pos].dx;
7103         int newy = y + check_xy[pos].dy;
7104         int new_move_dir = check_xy[pos].dir;
7105
7106         if (IS_PLAYER(newx, newy))
7107           break;
7108
7109         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7110         {
7111           MovDir[x][y] = new_move_dir;
7112           MovDelay[x][y] = level.android_move_time * 8 + 1;
7113
7114           break;
7115         }
7116       }
7117     }
7118   }
7119   else if (move_pattern == MV_TURNING_LEFT ||
7120            move_pattern == MV_TURNING_RIGHT ||
7121            move_pattern == MV_TURNING_LEFT_RIGHT ||
7122            move_pattern == MV_TURNING_RIGHT_LEFT ||
7123            move_pattern == MV_TURNING_RANDOM ||
7124            move_pattern == MV_ALL_DIRECTIONS)
7125   {
7126     boolean can_turn_left =
7127       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7128     boolean can_turn_right =
7129       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7130
7131     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7132       return;
7133
7134     if (move_pattern == MV_TURNING_LEFT)
7135       MovDir[x][y] = left_dir;
7136     else if (move_pattern == MV_TURNING_RIGHT)
7137       MovDir[x][y] = right_dir;
7138     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7139       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7140     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7141       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7142     else if (move_pattern == MV_TURNING_RANDOM)
7143       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7144                       can_turn_right && !can_turn_left ? right_dir :
7145                       RND(2) ? left_dir : right_dir);
7146     else if (can_turn_left && can_turn_right)
7147       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7148     else if (can_turn_left)
7149       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7150     else if (can_turn_right)
7151       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7152     else
7153       MovDir[x][y] = back_dir;
7154
7155     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7156   }
7157   else if (move_pattern == MV_HORIZONTAL ||
7158            move_pattern == MV_VERTICAL)
7159   {
7160     if (move_pattern & old_move_dir)
7161       MovDir[x][y] = back_dir;
7162     else if (move_pattern == MV_HORIZONTAL)
7163       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7164     else if (move_pattern == MV_VERTICAL)
7165       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7166
7167     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7168   }
7169   else if (move_pattern & MV_ANY_DIRECTION)
7170   {
7171     MovDir[x][y] = move_pattern;
7172     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7173   }
7174   else if (move_pattern & MV_WIND_DIRECTION)
7175   {
7176     MovDir[x][y] = game.wind_direction;
7177     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7178   }
7179   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7180   {
7181     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7182       MovDir[x][y] = left_dir;
7183     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7184       MovDir[x][y] = right_dir;
7185
7186     if (MovDir[x][y] != old_move_dir)
7187       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7188   }
7189   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7190   {
7191     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7192       MovDir[x][y] = right_dir;
7193     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7194       MovDir[x][y] = left_dir;
7195
7196     if (MovDir[x][y] != old_move_dir)
7197       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7198   }
7199   else if (move_pattern == MV_TOWARDS_PLAYER ||
7200            move_pattern == MV_AWAY_FROM_PLAYER)
7201   {
7202     int attr_x = -1, attr_y = -1;
7203     int newx, newy;
7204     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7205
7206     if (AllPlayersGone)
7207     {
7208       attr_x = ExitX;
7209       attr_y = ExitY;
7210     }
7211     else
7212     {
7213       int i;
7214
7215       for (i = 0; i < MAX_PLAYERS; i++)
7216       {
7217         struct PlayerInfo *player = &stored_player[i];
7218         int jx = player->jx, jy = player->jy;
7219
7220         if (!player->active)
7221           continue;
7222
7223         if (attr_x == -1 ||
7224             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7225         {
7226           attr_x = jx;
7227           attr_y = jy;
7228         }
7229       }
7230     }
7231
7232     MovDir[x][y] = MV_NONE;
7233     if (attr_x < x)
7234       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7235     else if (attr_x > x)
7236       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7237     if (attr_y < y)
7238       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7239     else if (attr_y > y)
7240       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7241
7242     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7243
7244     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7245     {
7246       boolean first_horiz = RND(2);
7247       int new_move_dir = MovDir[x][y];
7248
7249       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7250       {
7251         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7252         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7253
7254         return;
7255       }
7256
7257       MovDir[x][y] =
7258         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7259       Moving2Blocked(x, y, &newx, &newy);
7260
7261       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7262         return;
7263
7264       MovDir[x][y] =
7265         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7266       Moving2Blocked(x, y, &newx, &newy);
7267
7268       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7269         return;
7270
7271       MovDir[x][y] = old_move_dir;
7272     }
7273   }
7274   else if (move_pattern == MV_WHEN_PUSHED ||
7275            move_pattern == MV_WHEN_DROPPED)
7276   {
7277     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7278       MovDir[x][y] = MV_NONE;
7279
7280     MovDelay[x][y] = 0;
7281   }
7282   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7283   {
7284     static int test_xy[7][2] =
7285     {
7286       { 0, -1 },
7287       { -1, 0 },
7288       { +1, 0 },
7289       { 0, +1 },
7290       { 0, -1 },
7291       { -1, 0 },
7292       { +1, 0 },
7293     };
7294     static int test_dir[7] =
7295     {
7296       MV_UP,
7297       MV_LEFT,
7298       MV_RIGHT,
7299       MV_DOWN,
7300       MV_UP,
7301       MV_LEFT,
7302       MV_RIGHT,
7303     };
7304     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7305     int move_preference = -1000000;     /* start with very low preference */
7306     int new_move_dir = MV_NONE;
7307     int start_test = RND(4);
7308     int i;
7309
7310     for (i = 0; i < NUM_DIRECTIONS; i++)
7311     {
7312       int move_dir = test_dir[start_test + i];
7313       int move_dir_preference;
7314
7315       xx = x + test_xy[start_test + i][0];
7316       yy = y + test_xy[start_test + i][1];
7317
7318       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7319           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7320       {
7321         new_move_dir = move_dir;
7322
7323         break;
7324       }
7325
7326       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7327         continue;
7328
7329       move_dir_preference = -1 * RunnerVisit[xx][yy];
7330       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7331         move_dir_preference = PlayerVisit[xx][yy];
7332
7333       if (move_dir_preference > move_preference)
7334       {
7335         /* prefer field that has not been visited for the longest time */
7336         move_preference = move_dir_preference;
7337         new_move_dir = move_dir;
7338       }
7339       else if (move_dir_preference == move_preference &&
7340                move_dir == old_move_dir)
7341       {
7342         /* prefer last direction when all directions are preferred equally */
7343         move_preference = move_dir_preference;
7344         new_move_dir = move_dir;
7345       }
7346     }
7347
7348     MovDir[x][y] = new_move_dir;
7349     if (old_move_dir != new_move_dir)
7350       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7351   }
7352 }
7353
7354 static void TurnRound(int x, int y)
7355 {
7356   int direction = MovDir[x][y];
7357
7358   TurnRoundExt(x, y);
7359
7360   GfxDir[x][y] = MovDir[x][y];
7361
7362   if (direction != MovDir[x][y])
7363     GfxFrame[x][y] = 0;
7364
7365   if (MovDelay[x][y])
7366     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7367
7368   ResetGfxFrame(x, y);
7369 }
7370
7371 static boolean JustBeingPushed(int x, int y)
7372 {
7373   int i;
7374
7375   for (i = 0; i < MAX_PLAYERS; i++)
7376   {
7377     struct PlayerInfo *player = &stored_player[i];
7378
7379     if (player->active && player->is_pushing && player->MovPos)
7380     {
7381       int next_jx = player->jx + (player->jx - player->last_jx);
7382       int next_jy = player->jy + (player->jy - player->last_jy);
7383
7384       if (x == next_jx && y == next_jy)
7385         return TRUE;
7386     }
7387   }
7388
7389   return FALSE;
7390 }
7391
7392 void StartMoving(int x, int y)
7393 {
7394   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7395   int element = Feld[x][y];
7396
7397   if (Stop[x][y])
7398     return;
7399
7400   if (MovDelay[x][y] == 0)
7401     GfxAction[x][y] = ACTION_DEFAULT;
7402
7403   if (CAN_FALL(element) && y < lev_fieldy - 1)
7404   {
7405     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7406         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7407       if (JustBeingPushed(x, y))
7408         return;
7409
7410     if (element == EL_QUICKSAND_FULL)
7411     {
7412       if (IS_FREE(x, y + 1))
7413       {
7414         InitMovingField(x, y, MV_DOWN);
7415         started_moving = TRUE;
7416
7417         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7418 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7419         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7420           Store[x][y] = EL_ROCK;
7421 #else
7422         Store[x][y] = EL_ROCK;
7423 #endif
7424
7425         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7426       }
7427       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7428       {
7429         if (!MovDelay[x][y])
7430         {
7431           MovDelay[x][y] = TILEY + 1;
7432
7433           ResetGfxAnimation(x, y);
7434           ResetGfxAnimation(x, y + 1);
7435         }
7436
7437         if (MovDelay[x][y])
7438         {
7439           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7440           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7441
7442           MovDelay[x][y]--;
7443           if (MovDelay[x][y])
7444             return;
7445         }
7446
7447         Feld[x][y] = EL_QUICKSAND_EMPTY;
7448         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7449         Store[x][y + 1] = Store[x][y];
7450         Store[x][y] = 0;
7451
7452         PlayLevelSoundAction(x, y, ACTION_FILLING);
7453       }
7454       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7455       {
7456         if (!MovDelay[x][y])
7457         {
7458           MovDelay[x][y] = TILEY + 1;
7459
7460           ResetGfxAnimation(x, y);
7461           ResetGfxAnimation(x, y + 1);
7462         }
7463
7464         if (MovDelay[x][y])
7465         {
7466           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7467           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7468
7469           MovDelay[x][y]--;
7470           if (MovDelay[x][y])
7471             return;
7472         }
7473
7474         Feld[x][y] = EL_QUICKSAND_EMPTY;
7475         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7476         Store[x][y + 1] = Store[x][y];
7477         Store[x][y] = 0;
7478
7479         PlayLevelSoundAction(x, y, ACTION_FILLING);
7480       }
7481     }
7482     else if (element == EL_QUICKSAND_FAST_FULL)
7483     {
7484       if (IS_FREE(x, y + 1))
7485       {
7486         InitMovingField(x, y, MV_DOWN);
7487         started_moving = TRUE;
7488
7489         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7490 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7491         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7492           Store[x][y] = EL_ROCK;
7493 #else
7494         Store[x][y] = EL_ROCK;
7495 #endif
7496
7497         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7498       }
7499       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7500       {
7501         if (!MovDelay[x][y])
7502         {
7503           MovDelay[x][y] = TILEY + 1;
7504
7505           ResetGfxAnimation(x, y);
7506           ResetGfxAnimation(x, y + 1);
7507         }
7508
7509         if (MovDelay[x][y])
7510         {
7511           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7512           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7513
7514           MovDelay[x][y]--;
7515           if (MovDelay[x][y])
7516             return;
7517         }
7518
7519         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7520         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7521         Store[x][y + 1] = Store[x][y];
7522         Store[x][y] = 0;
7523
7524         PlayLevelSoundAction(x, y, ACTION_FILLING);
7525       }
7526       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7527       {
7528         if (!MovDelay[x][y])
7529         {
7530           MovDelay[x][y] = TILEY + 1;
7531
7532           ResetGfxAnimation(x, y);
7533           ResetGfxAnimation(x, y + 1);
7534         }
7535
7536         if (MovDelay[x][y])
7537         {
7538           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7539           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7540
7541           MovDelay[x][y]--;
7542           if (MovDelay[x][y])
7543             return;
7544         }
7545
7546         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7547         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7548         Store[x][y + 1] = Store[x][y];
7549         Store[x][y] = 0;
7550
7551         PlayLevelSoundAction(x, y, ACTION_FILLING);
7552       }
7553     }
7554     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7555              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7556     {
7557       InitMovingField(x, y, MV_DOWN);
7558       started_moving = TRUE;
7559
7560       Feld[x][y] = EL_QUICKSAND_FILLING;
7561       Store[x][y] = element;
7562
7563       PlayLevelSoundAction(x, y, ACTION_FILLING);
7564     }
7565     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7566              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7567     {
7568       InitMovingField(x, y, MV_DOWN);
7569       started_moving = TRUE;
7570
7571       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7572       Store[x][y] = element;
7573
7574       PlayLevelSoundAction(x, y, ACTION_FILLING);
7575     }
7576     else if (element == EL_MAGIC_WALL_FULL)
7577     {
7578       if (IS_FREE(x, y + 1))
7579       {
7580         InitMovingField(x, y, MV_DOWN);
7581         started_moving = TRUE;
7582
7583         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7584         Store[x][y] = EL_CHANGED(Store[x][y]);
7585       }
7586       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7587       {
7588         if (!MovDelay[x][y])
7589           MovDelay[x][y] = TILEY / 4 + 1;
7590
7591         if (MovDelay[x][y])
7592         {
7593           MovDelay[x][y]--;
7594           if (MovDelay[x][y])
7595             return;
7596         }
7597
7598         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7599         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7600         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7601         Store[x][y] = 0;
7602       }
7603     }
7604     else if (element == EL_BD_MAGIC_WALL_FULL)
7605     {
7606       if (IS_FREE(x, y + 1))
7607       {
7608         InitMovingField(x, y, MV_DOWN);
7609         started_moving = TRUE;
7610
7611         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7612         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7613       }
7614       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7615       {
7616         if (!MovDelay[x][y])
7617           MovDelay[x][y] = TILEY / 4 + 1;
7618
7619         if (MovDelay[x][y])
7620         {
7621           MovDelay[x][y]--;
7622           if (MovDelay[x][y])
7623             return;
7624         }
7625
7626         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7627         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7628         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7629         Store[x][y] = 0;
7630       }
7631     }
7632     else if (element == EL_DC_MAGIC_WALL_FULL)
7633     {
7634       if (IS_FREE(x, y + 1))
7635       {
7636         InitMovingField(x, y, MV_DOWN);
7637         started_moving = TRUE;
7638
7639         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7640         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7641       }
7642       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7643       {
7644         if (!MovDelay[x][y])
7645           MovDelay[x][y] = TILEY / 4 + 1;
7646
7647         if (MovDelay[x][y])
7648         {
7649           MovDelay[x][y]--;
7650           if (MovDelay[x][y])
7651             return;
7652         }
7653
7654         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7655         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7656         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7657         Store[x][y] = 0;
7658       }
7659     }
7660     else if ((CAN_PASS_MAGIC_WALL(element) &&
7661               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7662                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7663              (CAN_PASS_DC_MAGIC_WALL(element) &&
7664               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7665
7666     {
7667       InitMovingField(x, y, MV_DOWN);
7668       started_moving = TRUE;
7669
7670       Feld[x][y] =
7671         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7672          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7673          EL_DC_MAGIC_WALL_FILLING);
7674       Store[x][y] = element;
7675     }
7676     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7677     {
7678       SplashAcid(x, y + 1);
7679
7680       InitMovingField(x, y, MV_DOWN);
7681       started_moving = TRUE;
7682
7683       Store[x][y] = EL_ACID;
7684     }
7685     else if (
7686              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7687               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7688              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7689               CAN_FALL(element) && WasJustFalling[x][y] &&
7690               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7691
7692              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7693               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7694               (Feld[x][y + 1] == EL_BLOCKED)))
7695     {
7696       /* this is needed for a special case not covered by calling "Impact()"
7697          from "ContinueMoving()": if an element moves to a tile directly below
7698          another element which was just falling on that tile (which was empty
7699          in the previous frame), the falling element above would just stop
7700          instead of smashing the element below (in previous version, the above
7701          element was just checked for "moving" instead of "falling", resulting
7702          in incorrect smashes caused by horizontal movement of the above
7703          element; also, the case of the player being the element to smash was
7704          simply not covered here... :-/ ) */
7705
7706       CheckCollision[x][y] = 0;
7707       CheckImpact[x][y] = 0;
7708
7709       Impact(x, y);
7710     }
7711     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7712     {
7713       if (MovDir[x][y] == MV_NONE)
7714       {
7715         InitMovingField(x, y, MV_DOWN);
7716         started_moving = TRUE;
7717       }
7718     }
7719     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7720     {
7721       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7722         MovDir[x][y] = MV_DOWN;
7723
7724       InitMovingField(x, y, MV_DOWN);
7725       started_moving = TRUE;
7726     }
7727     else if (element == EL_AMOEBA_DROP)
7728     {
7729       Feld[x][y] = EL_AMOEBA_GROWING;
7730       Store[x][y] = EL_AMOEBA_WET;
7731     }
7732     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7733               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7734              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7735              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7736     {
7737       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7738                                 (IS_FREE(x - 1, y + 1) ||
7739                                  Feld[x - 1][y + 1] == EL_ACID));
7740       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7741                                 (IS_FREE(x + 1, y + 1) ||
7742                                  Feld[x + 1][y + 1] == EL_ACID));
7743       boolean can_fall_any  = (can_fall_left || can_fall_right);
7744       boolean can_fall_both = (can_fall_left && can_fall_right);
7745       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7746
7747       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7748       {
7749         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7750           can_fall_right = FALSE;
7751         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7752           can_fall_left = FALSE;
7753         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7754           can_fall_right = FALSE;
7755         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7756           can_fall_left = FALSE;
7757
7758         can_fall_any  = (can_fall_left || can_fall_right);
7759         can_fall_both = FALSE;
7760       }
7761
7762       if (can_fall_both)
7763       {
7764         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7765           can_fall_right = FALSE;       /* slip down on left side */
7766         else
7767           can_fall_left = !(can_fall_right = RND(2));
7768
7769         can_fall_both = FALSE;
7770       }
7771
7772       if (can_fall_any)
7773       {
7774         /* if not determined otherwise, prefer left side for slipping down */
7775         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7776         started_moving = TRUE;
7777       }
7778     }
7779     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7780     {
7781       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7782       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7783       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7784       int belt_dir = game.belt_dir[belt_nr];
7785
7786       if ((belt_dir == MV_LEFT  && left_is_free) ||
7787           (belt_dir == MV_RIGHT && right_is_free))
7788       {
7789         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7790
7791         InitMovingField(x, y, belt_dir);
7792         started_moving = TRUE;
7793
7794         Pushed[x][y] = TRUE;
7795         Pushed[nextx][y] = TRUE;
7796
7797         GfxAction[x][y] = ACTION_DEFAULT;
7798       }
7799       else
7800       {
7801         MovDir[x][y] = 0;       /* if element was moving, stop it */
7802       }
7803     }
7804   }
7805
7806   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7807   if (CAN_MOVE(element) && !started_moving)
7808   {
7809     int move_pattern = element_info[element].move_pattern;
7810     int newx, newy;
7811
7812     Moving2Blocked(x, y, &newx, &newy);
7813
7814     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7815       return;
7816
7817     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7818         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7819     {
7820       WasJustMoving[x][y] = 0;
7821       CheckCollision[x][y] = 0;
7822
7823       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7824
7825       if (Feld[x][y] != element)        /* element has changed */
7826         return;
7827     }
7828
7829     if (!MovDelay[x][y])        /* start new movement phase */
7830     {
7831       /* all objects that can change their move direction after each step
7832          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7833
7834       if (element != EL_YAMYAM &&
7835           element != EL_DARK_YAMYAM &&
7836           element != EL_PACMAN &&
7837           !(move_pattern & MV_ANY_DIRECTION) &&
7838           move_pattern != MV_TURNING_LEFT &&
7839           move_pattern != MV_TURNING_RIGHT &&
7840           move_pattern != MV_TURNING_LEFT_RIGHT &&
7841           move_pattern != MV_TURNING_RIGHT_LEFT &&
7842           move_pattern != MV_TURNING_RANDOM)
7843       {
7844         TurnRound(x, y);
7845
7846         if (MovDelay[x][y] && (element == EL_BUG ||
7847                                element == EL_SPACESHIP ||
7848                                element == EL_SP_SNIKSNAK ||
7849                                element == EL_SP_ELECTRON ||
7850                                element == EL_MOLE))
7851           TEST_DrawLevelField(x, y);
7852       }
7853     }
7854
7855     if (MovDelay[x][y])         /* wait some time before next movement */
7856     {
7857       MovDelay[x][y]--;
7858
7859       if (element == EL_ROBOT ||
7860           element == EL_YAMYAM ||
7861           element == EL_DARK_YAMYAM)
7862       {
7863         DrawLevelElementAnimationIfNeeded(x, y, element);
7864         PlayLevelSoundAction(x, y, ACTION_WAITING);
7865       }
7866       else if (element == EL_SP_ELECTRON)
7867         DrawLevelElementAnimationIfNeeded(x, y, element);
7868       else if (element == EL_DRAGON)
7869       {
7870         int i;
7871         int dir = MovDir[x][y];
7872         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7873         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7874         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7875                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7876                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7877                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7878         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7879
7880         GfxAction[x][y] = ACTION_ATTACKING;
7881
7882         if (IS_PLAYER(x, y))
7883           DrawPlayerField(x, y);
7884         else
7885           TEST_DrawLevelField(x, y);
7886
7887         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7888
7889         for (i = 1; i <= 3; i++)
7890         {
7891           int xx = x + i * dx;
7892           int yy = y + i * dy;
7893           int sx = SCREENX(xx);
7894           int sy = SCREENY(yy);
7895           int flame_graphic = graphic + (i - 1);
7896
7897           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7898             break;
7899
7900           if (MovDelay[x][y])
7901           {
7902             int flamed = MovingOrBlocked2Element(xx, yy);
7903
7904             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7905               Bang(xx, yy);
7906             else
7907               RemoveMovingField(xx, yy);
7908
7909             ChangeDelay[xx][yy] = 0;
7910
7911             Feld[xx][yy] = EL_FLAMES;
7912
7913             if (IN_SCR_FIELD(sx, sy))
7914             {
7915               TEST_DrawLevelFieldCrumbled(xx, yy);
7916               DrawGraphic(sx, sy, flame_graphic, frame);
7917             }
7918           }
7919           else
7920           {
7921             if (Feld[xx][yy] == EL_FLAMES)
7922               Feld[xx][yy] = EL_EMPTY;
7923             TEST_DrawLevelField(xx, yy);
7924           }
7925         }
7926       }
7927
7928       if (MovDelay[x][y])       /* element still has to wait some time */
7929       {
7930         PlayLevelSoundAction(x, y, ACTION_WAITING);
7931
7932         return;
7933       }
7934     }
7935
7936     /* now make next step */
7937
7938     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7939
7940     if (DONT_COLLIDE_WITH(element) &&
7941         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7942         !PLAYER_ENEMY_PROTECTED(newx, newy))
7943     {
7944       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7945
7946       return;
7947     }
7948
7949     else if (CAN_MOVE_INTO_ACID(element) &&
7950              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7951              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7952              (MovDir[x][y] == MV_DOWN ||
7953               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7954     {
7955       SplashAcid(newx, newy);
7956       Store[x][y] = EL_ACID;
7957     }
7958     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7959     {
7960       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7961           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7962           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7963           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7964       {
7965         RemoveField(x, y);
7966         TEST_DrawLevelField(x, y);
7967
7968         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7969         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7970           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7971
7972         local_player->friends_still_needed--;
7973         if (!local_player->friends_still_needed &&
7974             !local_player->GameOver && AllPlayersGone)
7975           PlayerWins(local_player);
7976
7977         return;
7978       }
7979       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7980       {
7981         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7982           TEST_DrawLevelField(newx, newy);
7983         else
7984           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7985       }
7986       else if (!IS_FREE(newx, newy))
7987       {
7988         GfxAction[x][y] = ACTION_WAITING;
7989
7990         if (IS_PLAYER(x, y))
7991           DrawPlayerField(x, y);
7992         else
7993           TEST_DrawLevelField(x, y);
7994
7995         return;
7996       }
7997     }
7998     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7999     {
8000       if (IS_FOOD_PIG(Feld[newx][newy]))
8001       {
8002         if (IS_MOVING(newx, newy))
8003           RemoveMovingField(newx, newy);
8004         else
8005         {
8006           Feld[newx][newy] = EL_EMPTY;
8007           TEST_DrawLevelField(newx, newy);
8008         }
8009
8010         PlayLevelSound(x, y, SND_PIG_DIGGING);
8011       }
8012       else if (!IS_FREE(newx, newy))
8013       {
8014         if (IS_PLAYER(x, y))
8015           DrawPlayerField(x, y);
8016         else
8017           TEST_DrawLevelField(x, y);
8018
8019         return;
8020       }
8021     }
8022     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8023     {
8024       if (Store[x][y] != EL_EMPTY)
8025       {
8026         boolean can_clone = FALSE;
8027         int xx, yy;
8028
8029         /* check if element to clone is still there */
8030         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8031         {
8032           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8033           {
8034             can_clone = TRUE;
8035
8036             break;
8037           }
8038         }
8039
8040         /* cannot clone or target field not free anymore -- do not clone */
8041         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8042           Store[x][y] = EL_EMPTY;
8043       }
8044
8045       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8046       {
8047         if (IS_MV_DIAGONAL(MovDir[x][y]))
8048         {
8049           int diagonal_move_dir = MovDir[x][y];
8050           int stored = Store[x][y];
8051           int change_delay = 8;
8052           int graphic;
8053
8054           /* android is moving diagonally */
8055
8056           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8057
8058           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8059           GfxElement[x][y] = EL_EMC_ANDROID;
8060           GfxAction[x][y] = ACTION_SHRINKING;
8061           GfxDir[x][y] = diagonal_move_dir;
8062           ChangeDelay[x][y] = change_delay;
8063
8064           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8065                                    GfxDir[x][y]);
8066
8067           DrawLevelGraphicAnimation(x, y, graphic);
8068           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8069
8070           if (Feld[newx][newy] == EL_ACID)
8071           {
8072             SplashAcid(newx, newy);
8073
8074             return;
8075           }
8076
8077           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8078
8079           Store[newx][newy] = EL_EMC_ANDROID;
8080           GfxElement[newx][newy] = EL_EMC_ANDROID;
8081           GfxAction[newx][newy] = ACTION_GROWING;
8082           GfxDir[newx][newy] = diagonal_move_dir;
8083           ChangeDelay[newx][newy] = change_delay;
8084
8085           graphic = el_act_dir2img(GfxElement[newx][newy],
8086                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8087
8088           DrawLevelGraphicAnimation(newx, newy, graphic);
8089           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8090
8091           return;
8092         }
8093         else
8094         {
8095           Feld[newx][newy] = EL_EMPTY;
8096           TEST_DrawLevelField(newx, newy);
8097
8098           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8099         }
8100       }
8101       else if (!IS_FREE(newx, newy))
8102       {
8103         return;
8104       }
8105     }
8106     else if (IS_CUSTOM_ELEMENT(element) &&
8107              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8108     {
8109       if (!DigFieldByCE(newx, newy, element))
8110         return;
8111
8112       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8113       {
8114         RunnerVisit[x][y] = FrameCounter;
8115         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8116       }
8117     }
8118     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8119     {
8120       if (!IS_FREE(newx, newy))
8121       {
8122         if (IS_PLAYER(x, y))
8123           DrawPlayerField(x, y);
8124         else
8125           TEST_DrawLevelField(x, y);
8126
8127         return;
8128       }
8129       else
8130       {
8131         boolean wanna_flame = !RND(10);
8132         int dx = newx - x, dy = newy - y;
8133         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8134         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8135         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8136                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8137         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8138                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8139
8140         if ((wanna_flame ||
8141              IS_CLASSIC_ENEMY(element1) ||
8142              IS_CLASSIC_ENEMY(element2)) &&
8143             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8144             element1 != EL_FLAMES && element2 != EL_FLAMES)
8145         {
8146           ResetGfxAnimation(x, y);
8147           GfxAction[x][y] = ACTION_ATTACKING;
8148
8149           if (IS_PLAYER(x, y))
8150             DrawPlayerField(x, y);
8151           else
8152             TEST_DrawLevelField(x, y);
8153
8154           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8155
8156           MovDelay[x][y] = 50;
8157
8158           Feld[newx][newy] = EL_FLAMES;
8159           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8160             Feld[newx1][newy1] = EL_FLAMES;
8161           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8162             Feld[newx2][newy2] = EL_FLAMES;
8163
8164           return;
8165         }
8166       }
8167     }
8168     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8169              Feld[newx][newy] == EL_DIAMOND)
8170     {
8171       if (IS_MOVING(newx, newy))
8172         RemoveMovingField(newx, newy);
8173       else
8174       {
8175         Feld[newx][newy] = EL_EMPTY;
8176         TEST_DrawLevelField(newx, newy);
8177       }
8178
8179       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8180     }
8181     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8182              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8183     {
8184       if (AmoebaNr[newx][newy])
8185       {
8186         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8187         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8188             Feld[newx][newy] == EL_BD_AMOEBA)
8189           AmoebaCnt[AmoebaNr[newx][newy]]--;
8190       }
8191
8192       if (IS_MOVING(newx, newy))
8193       {
8194         RemoveMovingField(newx, newy);
8195       }
8196       else
8197       {
8198         Feld[newx][newy] = EL_EMPTY;
8199         TEST_DrawLevelField(newx, newy);
8200       }
8201
8202       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8203     }
8204     else if ((element == EL_PACMAN || element == EL_MOLE)
8205              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8206     {
8207       if (AmoebaNr[newx][newy])
8208       {
8209         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8210         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8211             Feld[newx][newy] == EL_BD_AMOEBA)
8212           AmoebaCnt[AmoebaNr[newx][newy]]--;
8213       }
8214
8215       if (element == EL_MOLE)
8216       {
8217         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8218         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8219
8220         ResetGfxAnimation(x, y);
8221         GfxAction[x][y] = ACTION_DIGGING;
8222         TEST_DrawLevelField(x, y);
8223
8224         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8225
8226         return;                         /* wait for shrinking amoeba */
8227       }
8228       else      /* element == EL_PACMAN */
8229       {
8230         Feld[newx][newy] = EL_EMPTY;
8231         TEST_DrawLevelField(newx, newy);
8232         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8233       }
8234     }
8235     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8236              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8237               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8238     {
8239       /* wait for shrinking amoeba to completely disappear */
8240       return;
8241     }
8242     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8243     {
8244       /* object was running against a wall */
8245
8246       TurnRound(x, y);
8247
8248       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8249         DrawLevelElementAnimation(x, y, element);
8250
8251       if (DONT_TOUCH(element))
8252         TestIfBadThingTouchesPlayer(x, y);
8253
8254       return;
8255     }
8256
8257     InitMovingField(x, y, MovDir[x][y]);
8258
8259     PlayLevelSoundAction(x, y, ACTION_MOVING);
8260   }
8261
8262   if (MovDir[x][y])
8263     ContinueMoving(x, y);
8264 }
8265
8266 void ContinueMoving(int x, int y)
8267 {
8268   int element = Feld[x][y];
8269   struct ElementInfo *ei = &element_info[element];
8270   int direction = MovDir[x][y];
8271   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8272   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8273   int newx = x + dx, newy = y + dy;
8274   int stored = Store[x][y];
8275   int stored_new = Store[newx][newy];
8276   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8277   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8278   boolean last_line = (newy == lev_fieldy - 1);
8279
8280   MovPos[x][y] += getElementMoveStepsize(x, y);
8281
8282   if (pushed_by_player) /* special case: moving object pushed by player */
8283     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8284
8285   if (ABS(MovPos[x][y]) < TILEX)
8286   {
8287     TEST_DrawLevelField(x, y);
8288
8289     return;     /* element is still moving */
8290   }
8291
8292   /* element reached destination field */
8293
8294   Feld[x][y] = EL_EMPTY;
8295   Feld[newx][newy] = element;
8296   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8297
8298   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8299   {
8300     element = Feld[newx][newy] = EL_ACID;
8301   }
8302   else if (element == EL_MOLE)
8303   {
8304     Feld[x][y] = EL_SAND;
8305
8306     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8307   }
8308   else if (element == EL_QUICKSAND_FILLING)
8309   {
8310     element = Feld[newx][newy] = get_next_element(element);
8311     Store[newx][newy] = Store[x][y];
8312   }
8313   else if (element == EL_QUICKSAND_EMPTYING)
8314   {
8315     Feld[x][y] = get_next_element(element);
8316     element = Feld[newx][newy] = Store[x][y];
8317   }
8318   else if (element == EL_QUICKSAND_FAST_FILLING)
8319   {
8320     element = Feld[newx][newy] = get_next_element(element);
8321     Store[newx][newy] = Store[x][y];
8322   }
8323   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8324   {
8325     Feld[x][y] = get_next_element(element);
8326     element = Feld[newx][newy] = Store[x][y];
8327   }
8328   else if (element == EL_MAGIC_WALL_FILLING)
8329   {
8330     element = Feld[newx][newy] = get_next_element(element);
8331     if (!game.magic_wall_active)
8332       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8333     Store[newx][newy] = Store[x][y];
8334   }
8335   else if (element == EL_MAGIC_WALL_EMPTYING)
8336   {
8337     Feld[x][y] = get_next_element(element);
8338     if (!game.magic_wall_active)
8339       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8340     element = Feld[newx][newy] = Store[x][y];
8341
8342     InitField(newx, newy, FALSE);
8343   }
8344   else if (element == EL_BD_MAGIC_WALL_FILLING)
8345   {
8346     element = Feld[newx][newy] = get_next_element(element);
8347     if (!game.magic_wall_active)
8348       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8349     Store[newx][newy] = Store[x][y];
8350   }
8351   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8352   {
8353     Feld[x][y] = get_next_element(element);
8354     if (!game.magic_wall_active)
8355       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8356     element = Feld[newx][newy] = Store[x][y];
8357
8358     InitField(newx, newy, FALSE);
8359   }
8360   else if (element == EL_DC_MAGIC_WALL_FILLING)
8361   {
8362     element = Feld[newx][newy] = get_next_element(element);
8363     if (!game.magic_wall_active)
8364       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8365     Store[newx][newy] = Store[x][y];
8366   }
8367   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8368   {
8369     Feld[x][y] = get_next_element(element);
8370     if (!game.magic_wall_active)
8371       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8372     element = Feld[newx][newy] = Store[x][y];
8373
8374     InitField(newx, newy, FALSE);
8375   }
8376   else if (element == EL_AMOEBA_DROPPING)
8377   {
8378     Feld[x][y] = get_next_element(element);
8379     element = Feld[newx][newy] = Store[x][y];
8380   }
8381   else if (element == EL_SOKOBAN_OBJECT)
8382   {
8383     if (Back[x][y])
8384       Feld[x][y] = Back[x][y];
8385
8386     if (Back[newx][newy])
8387       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8388
8389     Back[x][y] = Back[newx][newy] = 0;
8390   }
8391
8392   Store[x][y] = EL_EMPTY;
8393   MovPos[x][y] = 0;
8394   MovDir[x][y] = 0;
8395   MovDelay[x][y] = 0;
8396
8397   MovDelay[newx][newy] = 0;
8398
8399   if (CAN_CHANGE_OR_HAS_ACTION(element))
8400   {
8401     /* copy element change control values to new field */
8402     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8403     ChangePage[newx][newy]  = ChangePage[x][y];
8404     ChangeCount[newx][newy] = ChangeCount[x][y];
8405     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8406   }
8407
8408   CustomValue[newx][newy] = CustomValue[x][y];
8409
8410   ChangeDelay[x][y] = 0;
8411   ChangePage[x][y] = -1;
8412   ChangeCount[x][y] = 0;
8413   ChangeEvent[x][y] = -1;
8414
8415   CustomValue[x][y] = 0;
8416
8417   /* copy animation control values to new field */
8418   GfxFrame[newx][newy]  = GfxFrame[x][y];
8419   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8420   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8421   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8422
8423   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8424
8425   /* some elements can leave other elements behind after moving */
8426   if (ei->move_leave_element != EL_EMPTY &&
8427       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8428       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8429   {
8430     int move_leave_element = ei->move_leave_element;
8431
8432     /* this makes it possible to leave the removed element again */
8433     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8434       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8435
8436     Feld[x][y] = move_leave_element;
8437
8438     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8439       MovDir[x][y] = direction;
8440
8441     InitField(x, y, FALSE);
8442
8443     if (GFX_CRUMBLED(Feld[x][y]))
8444       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8445
8446     if (ELEM_IS_PLAYER(move_leave_element))
8447       RelocatePlayer(x, y, move_leave_element);
8448   }
8449
8450   /* do this after checking for left-behind element */
8451   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8452
8453   if (!CAN_MOVE(element) ||
8454       (CAN_FALL(element) && direction == MV_DOWN &&
8455        (element == EL_SPRING ||
8456         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8457         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8458     GfxDir[x][y] = MovDir[newx][newy] = 0;
8459
8460   TEST_DrawLevelField(x, y);
8461   TEST_DrawLevelField(newx, newy);
8462
8463   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8464
8465   /* prevent pushed element from moving on in pushed direction */
8466   if (pushed_by_player && CAN_MOVE(element) &&
8467       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8468       !(element_info[element].move_pattern & direction))
8469     TurnRound(newx, newy);
8470
8471   /* prevent elements on conveyor belt from moving on in last direction */
8472   if (pushed_by_conveyor && CAN_FALL(element) &&
8473       direction & MV_HORIZONTAL)
8474     MovDir[newx][newy] = 0;
8475
8476   if (!pushed_by_player)
8477   {
8478     int nextx = newx + dx, nexty = newy + dy;
8479     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8480
8481     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8482
8483     if (CAN_FALL(element) && direction == MV_DOWN)
8484       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8485
8486     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8487       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8488
8489     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8490       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8491   }
8492
8493   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8494   {
8495     TestIfBadThingTouchesPlayer(newx, newy);
8496     TestIfBadThingTouchesFriend(newx, newy);
8497
8498     if (!IS_CUSTOM_ELEMENT(element))
8499       TestIfBadThingTouchesOtherBadThing(newx, newy);
8500   }
8501   else if (element == EL_PENGUIN)
8502     TestIfFriendTouchesBadThing(newx, newy);
8503
8504   if (DONT_GET_HIT_BY(element))
8505   {
8506     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8507   }
8508
8509   /* give the player one last chance (one more frame) to move away */
8510   if (CAN_FALL(element) && direction == MV_DOWN &&
8511       (last_line || (!IS_FREE(x, newy + 1) &&
8512                      (!IS_PLAYER(x, newy + 1) ||
8513                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8514     Impact(x, newy);
8515
8516   if (pushed_by_player && !game.use_change_when_pushing_bug)
8517   {
8518     int push_side = MV_DIR_OPPOSITE(direction);
8519     struct PlayerInfo *player = PLAYERINFO(x, y);
8520
8521     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8522                                player->index_bit, push_side);
8523     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8524                                         player->index_bit, push_side);
8525   }
8526
8527   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8528     MovDelay[newx][newy] = 1;
8529
8530   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8531
8532   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8533   TestIfElementHitsCustomElement(newx, newy, direction);
8534   TestIfPlayerTouchesCustomElement(newx, newy);
8535   TestIfElementTouchesCustomElement(newx, newy);
8536
8537   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8538       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8539     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8540                              MV_DIR_OPPOSITE(direction));
8541 }
8542
8543 int AmoebeNachbarNr(int ax, int ay)
8544 {
8545   int i;
8546   int element = Feld[ax][ay];
8547   int group_nr = 0;
8548   static int xy[4][2] =
8549   {
8550     { 0, -1 },
8551     { -1, 0 },
8552     { +1, 0 },
8553     { 0, +1 }
8554   };
8555
8556   for (i = 0; i < NUM_DIRECTIONS; i++)
8557   {
8558     int x = ax + xy[i][0];
8559     int y = ay + xy[i][1];
8560
8561     if (!IN_LEV_FIELD(x, y))
8562       continue;
8563
8564     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8565       group_nr = AmoebaNr[x][y];
8566   }
8567
8568   return group_nr;
8569 }
8570
8571 void AmoebenVereinigen(int ax, int ay)
8572 {
8573   int i, x, y, xx, yy;
8574   int new_group_nr = AmoebaNr[ax][ay];
8575   static int xy[4][2] =
8576   {
8577     { 0, -1 },
8578     { -1, 0 },
8579     { +1, 0 },
8580     { 0, +1 }
8581   };
8582
8583   if (new_group_nr == 0)
8584     return;
8585
8586   for (i = 0; i < NUM_DIRECTIONS; i++)
8587   {
8588     x = ax + xy[i][0];
8589     y = ay + xy[i][1];
8590
8591     if (!IN_LEV_FIELD(x, y))
8592       continue;
8593
8594     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8595          Feld[x][y] == EL_BD_AMOEBA ||
8596          Feld[x][y] == EL_AMOEBA_DEAD) &&
8597         AmoebaNr[x][y] != new_group_nr)
8598     {
8599       int old_group_nr = AmoebaNr[x][y];
8600
8601       if (old_group_nr == 0)
8602         return;
8603
8604       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8605       AmoebaCnt[old_group_nr] = 0;
8606       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8607       AmoebaCnt2[old_group_nr] = 0;
8608
8609       SCAN_PLAYFIELD(xx, yy)
8610       {
8611         if (AmoebaNr[xx][yy] == old_group_nr)
8612           AmoebaNr[xx][yy] = new_group_nr;
8613       }
8614     }
8615   }
8616 }
8617
8618 void AmoebeUmwandeln(int ax, int ay)
8619 {
8620   int i, x, y;
8621
8622   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8623   {
8624     int group_nr = AmoebaNr[ax][ay];
8625
8626 #ifdef DEBUG
8627     if (group_nr == 0)
8628     {
8629       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8630       printf("AmoebeUmwandeln(): This should never happen!\n");
8631       return;
8632     }
8633 #endif
8634
8635     SCAN_PLAYFIELD(x, y)
8636     {
8637       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8638       {
8639         AmoebaNr[x][y] = 0;
8640         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8641       }
8642     }
8643
8644     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8645                             SND_AMOEBA_TURNING_TO_GEM :
8646                             SND_AMOEBA_TURNING_TO_ROCK));
8647     Bang(ax, ay);
8648   }
8649   else
8650   {
8651     static int xy[4][2] =
8652     {
8653       { 0, -1 },
8654       { -1, 0 },
8655       { +1, 0 },
8656       { 0, +1 }
8657     };
8658
8659     for (i = 0; i < NUM_DIRECTIONS; i++)
8660     {
8661       x = ax + xy[i][0];
8662       y = ay + xy[i][1];
8663
8664       if (!IN_LEV_FIELD(x, y))
8665         continue;
8666
8667       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8668       {
8669         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8670                               SND_AMOEBA_TURNING_TO_GEM :
8671                               SND_AMOEBA_TURNING_TO_ROCK));
8672         Bang(x, y);
8673       }
8674     }
8675   }
8676 }
8677
8678 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8679 {
8680   int x, y;
8681   int group_nr = AmoebaNr[ax][ay];
8682   boolean done = FALSE;
8683
8684 #ifdef DEBUG
8685   if (group_nr == 0)
8686   {
8687     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8688     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8689     return;
8690   }
8691 #endif
8692
8693   SCAN_PLAYFIELD(x, y)
8694   {
8695     if (AmoebaNr[x][y] == group_nr &&
8696         (Feld[x][y] == EL_AMOEBA_DEAD ||
8697          Feld[x][y] == EL_BD_AMOEBA ||
8698          Feld[x][y] == EL_AMOEBA_GROWING))
8699     {
8700       AmoebaNr[x][y] = 0;
8701       Feld[x][y] = new_element;
8702       InitField(x, y, FALSE);
8703       TEST_DrawLevelField(x, y);
8704       done = TRUE;
8705     }
8706   }
8707
8708   if (done)
8709     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8710                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8711                             SND_BD_AMOEBA_TURNING_TO_GEM));
8712 }
8713
8714 void AmoebeWaechst(int x, int y)
8715 {
8716   static unsigned int sound_delay = 0;
8717   static unsigned int sound_delay_value = 0;
8718
8719   if (!MovDelay[x][y])          /* start new growing cycle */
8720   {
8721     MovDelay[x][y] = 7;
8722
8723     if (DelayReached(&sound_delay, sound_delay_value))
8724     {
8725       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8726       sound_delay_value = 30;
8727     }
8728   }
8729
8730   if (MovDelay[x][y])           /* wait some time before growing bigger */
8731   {
8732     MovDelay[x][y]--;
8733     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8734     {
8735       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8736                                            6 - MovDelay[x][y]);
8737
8738       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8739     }
8740
8741     if (!MovDelay[x][y])
8742     {
8743       Feld[x][y] = Store[x][y];
8744       Store[x][y] = 0;
8745       TEST_DrawLevelField(x, y);
8746     }
8747   }
8748 }
8749
8750 void AmoebaDisappearing(int x, int y)
8751 {
8752   static unsigned int sound_delay = 0;
8753   static unsigned int sound_delay_value = 0;
8754
8755   if (!MovDelay[x][y])          /* start new shrinking cycle */
8756   {
8757     MovDelay[x][y] = 7;
8758
8759     if (DelayReached(&sound_delay, sound_delay_value))
8760       sound_delay_value = 30;
8761   }
8762
8763   if (MovDelay[x][y])           /* wait some time before shrinking */
8764   {
8765     MovDelay[x][y]--;
8766     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8767     {
8768       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8769                                            6 - MovDelay[x][y]);
8770
8771       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8772     }
8773
8774     if (!MovDelay[x][y])
8775     {
8776       Feld[x][y] = EL_EMPTY;
8777       TEST_DrawLevelField(x, y);
8778
8779       /* don't let mole enter this field in this cycle;
8780          (give priority to objects falling to this field from above) */
8781       Stop[x][y] = TRUE;
8782     }
8783   }
8784 }
8785
8786 void AmoebeAbleger(int ax, int ay)
8787 {
8788   int i;
8789   int element = Feld[ax][ay];
8790   int graphic = el2img(element);
8791   int newax = ax, neway = ay;
8792   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8793   static int xy[4][2] =
8794   {
8795     { 0, -1 },
8796     { -1, 0 },
8797     { +1, 0 },
8798     { 0, +1 }
8799   };
8800
8801   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8802   {
8803     Feld[ax][ay] = EL_AMOEBA_DEAD;
8804     TEST_DrawLevelField(ax, ay);
8805     return;
8806   }
8807
8808   if (IS_ANIMATED(graphic))
8809     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8810
8811   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8812     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8813
8814   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8815   {
8816     MovDelay[ax][ay]--;
8817     if (MovDelay[ax][ay])
8818       return;
8819   }
8820
8821   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8822   {
8823     int start = RND(4);
8824     int x = ax + xy[start][0];
8825     int y = ay + xy[start][1];
8826
8827     if (!IN_LEV_FIELD(x, y))
8828       return;
8829
8830     if (IS_FREE(x, y) ||
8831         CAN_GROW_INTO(Feld[x][y]) ||
8832         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8833         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8834     {
8835       newax = x;
8836       neway = y;
8837     }
8838
8839     if (newax == ax && neway == ay)
8840       return;
8841   }
8842   else                          /* normal or "filled" (BD style) amoeba */
8843   {
8844     int start = RND(4);
8845     boolean waiting_for_player = FALSE;
8846
8847     for (i = 0; i < NUM_DIRECTIONS; i++)
8848     {
8849       int j = (start + i) % 4;
8850       int x = ax + xy[j][0];
8851       int y = ay + xy[j][1];
8852
8853       if (!IN_LEV_FIELD(x, y))
8854         continue;
8855
8856       if (IS_FREE(x, y) ||
8857           CAN_GROW_INTO(Feld[x][y]) ||
8858           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8859           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8860       {
8861         newax = x;
8862         neway = y;
8863         break;
8864       }
8865       else if (IS_PLAYER(x, y))
8866         waiting_for_player = TRUE;
8867     }
8868
8869     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8870     {
8871       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8872       {
8873         Feld[ax][ay] = EL_AMOEBA_DEAD;
8874         TEST_DrawLevelField(ax, ay);
8875         AmoebaCnt[AmoebaNr[ax][ay]]--;
8876
8877         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8878         {
8879           if (element == EL_AMOEBA_FULL)
8880             AmoebeUmwandeln(ax, ay);
8881           else if (element == EL_BD_AMOEBA)
8882             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8883         }
8884       }
8885       return;
8886     }
8887     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8888     {
8889       /* amoeba gets larger by growing in some direction */
8890
8891       int new_group_nr = AmoebaNr[ax][ay];
8892
8893 #ifdef DEBUG
8894   if (new_group_nr == 0)
8895   {
8896     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8897     printf("AmoebeAbleger(): This should never happen!\n");
8898     return;
8899   }
8900 #endif
8901
8902       AmoebaNr[newax][neway] = new_group_nr;
8903       AmoebaCnt[new_group_nr]++;
8904       AmoebaCnt2[new_group_nr]++;
8905
8906       /* if amoeba touches other amoeba(s) after growing, unify them */
8907       AmoebenVereinigen(newax, neway);
8908
8909       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8910       {
8911         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8912         return;
8913       }
8914     }
8915   }
8916
8917   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8918       (neway == lev_fieldy - 1 && newax != ax))
8919   {
8920     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8921     Store[newax][neway] = element;
8922   }
8923   else if (neway == ay || element == EL_EMC_DRIPPER)
8924   {
8925     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8926
8927     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8928   }
8929   else
8930   {
8931     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8932     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8933     Store[ax][ay] = EL_AMOEBA_DROP;
8934     ContinueMoving(ax, ay);
8935     return;
8936   }
8937
8938   TEST_DrawLevelField(newax, neway);
8939 }
8940
8941 void Life(int ax, int ay)
8942 {
8943   int x1, y1, x2, y2;
8944   int life_time = 40;
8945   int element = Feld[ax][ay];
8946   int graphic = el2img(element);
8947   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8948                          level.biomaze);
8949   boolean changed = FALSE;
8950
8951   if (IS_ANIMATED(graphic))
8952     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8953
8954   if (Stop[ax][ay])
8955     return;
8956
8957   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8958     MovDelay[ax][ay] = life_time;
8959
8960   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8961   {
8962     MovDelay[ax][ay]--;
8963     if (MovDelay[ax][ay])
8964       return;
8965   }
8966
8967   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8968   {
8969     int xx = ax+x1, yy = ay+y1;
8970     int nachbarn = 0;
8971
8972     if (!IN_LEV_FIELD(xx, yy))
8973       continue;
8974
8975     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8976     {
8977       int x = xx+x2, y = yy+y2;
8978
8979       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8980         continue;
8981
8982       if (((Feld[x][y] == element ||
8983             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8984            !Stop[x][y]) ||
8985           (IS_FREE(x, y) && Stop[x][y]))
8986         nachbarn++;
8987     }
8988
8989     if (xx == ax && yy == ay)           /* field in the middle */
8990     {
8991       if (nachbarn < life_parameter[0] ||
8992           nachbarn > life_parameter[1])
8993       {
8994         Feld[xx][yy] = EL_EMPTY;
8995         if (!Stop[xx][yy])
8996           TEST_DrawLevelField(xx, yy);
8997         Stop[xx][yy] = TRUE;
8998         changed = TRUE;
8999       }
9000     }
9001     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9002     {                                   /* free border field */
9003       if (nachbarn >= life_parameter[2] &&
9004           nachbarn <= life_parameter[3])
9005       {
9006         Feld[xx][yy] = element;
9007         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9008         if (!Stop[xx][yy])
9009           TEST_DrawLevelField(xx, yy);
9010         Stop[xx][yy] = TRUE;
9011         changed = TRUE;
9012       }
9013     }
9014   }
9015
9016   if (changed)
9017     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9018                    SND_GAME_OF_LIFE_GROWING);
9019 }
9020
9021 static void InitRobotWheel(int x, int y)
9022 {
9023   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9024 }
9025
9026 static void RunRobotWheel(int x, int y)
9027 {
9028   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9029 }
9030
9031 static void StopRobotWheel(int x, int y)
9032 {
9033   if (ZX == x && ZY == y)
9034   {
9035     ZX = ZY = -1;
9036
9037     game.robot_wheel_active = FALSE;
9038   }
9039 }
9040
9041 static void InitTimegateWheel(int x, int y)
9042 {
9043   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9044 }
9045
9046 static void RunTimegateWheel(int x, int y)
9047 {
9048   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9049 }
9050
9051 static void InitMagicBallDelay(int x, int y)
9052 {
9053   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9054 }
9055
9056 static void ActivateMagicBall(int bx, int by)
9057 {
9058   int x, y;
9059
9060   if (level.ball_random)
9061   {
9062     int pos_border = RND(8);    /* select one of the eight border elements */
9063     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9064     int xx = pos_content % 3;
9065     int yy = pos_content / 3;
9066
9067     x = bx - 1 + xx;
9068     y = by - 1 + yy;
9069
9070     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9071       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9072   }
9073   else
9074   {
9075     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9076     {
9077       int xx = x - bx + 1;
9078       int yy = y - by + 1;
9079
9080       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9081         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9082     }
9083   }
9084
9085   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9086 }
9087
9088 void CheckExit(int x, int y)
9089 {
9090   if (local_player->gems_still_needed > 0 ||
9091       local_player->sokobanfields_still_needed > 0 ||
9092       local_player->lights_still_needed > 0)
9093   {
9094     int element = Feld[x][y];
9095     int graphic = el2img(element);
9096
9097     if (IS_ANIMATED(graphic))
9098       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9099
9100     return;
9101   }
9102
9103   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9104     return;
9105
9106   Feld[x][y] = EL_EXIT_OPENING;
9107
9108   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9109 }
9110
9111 void CheckExitEM(int x, int y)
9112 {
9113   if (local_player->gems_still_needed > 0 ||
9114       local_player->sokobanfields_still_needed > 0 ||
9115       local_player->lights_still_needed > 0)
9116   {
9117     int element = Feld[x][y];
9118     int graphic = el2img(element);
9119
9120     if (IS_ANIMATED(graphic))
9121       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9122
9123     return;
9124   }
9125
9126   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9127     return;
9128
9129   Feld[x][y] = EL_EM_EXIT_OPENING;
9130
9131   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9132 }
9133
9134 void CheckExitSteel(int x, int y)
9135 {
9136   if (local_player->gems_still_needed > 0 ||
9137       local_player->sokobanfields_still_needed > 0 ||
9138       local_player->lights_still_needed > 0)
9139   {
9140     int element = Feld[x][y];
9141     int graphic = el2img(element);
9142
9143     if (IS_ANIMATED(graphic))
9144       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9145
9146     return;
9147   }
9148
9149   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9150     return;
9151
9152   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9153
9154   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9155 }
9156
9157 void CheckExitSteelEM(int x, int y)
9158 {
9159   if (local_player->gems_still_needed > 0 ||
9160       local_player->sokobanfields_still_needed > 0 ||
9161       local_player->lights_still_needed > 0)
9162   {
9163     int element = Feld[x][y];
9164     int graphic = el2img(element);
9165
9166     if (IS_ANIMATED(graphic))
9167       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9168
9169     return;
9170   }
9171
9172   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9173     return;
9174
9175   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9176
9177   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9178 }
9179
9180 void CheckExitSP(int x, int y)
9181 {
9182   if (local_player->gems_still_needed > 0)
9183   {
9184     int element = Feld[x][y];
9185     int graphic = el2img(element);
9186
9187     if (IS_ANIMATED(graphic))
9188       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9189
9190     return;
9191   }
9192
9193   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9194     return;
9195
9196   Feld[x][y] = EL_SP_EXIT_OPENING;
9197
9198   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9199 }
9200
9201 static void CloseAllOpenTimegates()
9202 {
9203   int x, y;
9204
9205   SCAN_PLAYFIELD(x, y)
9206   {
9207     int element = Feld[x][y];
9208
9209     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9210     {
9211       Feld[x][y] = EL_TIMEGATE_CLOSING;
9212
9213       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9214     }
9215   }
9216 }
9217
9218 void DrawTwinkleOnField(int x, int y)
9219 {
9220   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9221     return;
9222
9223   if (Feld[x][y] == EL_BD_DIAMOND)
9224     return;
9225
9226   if (MovDelay[x][y] == 0)      /* next animation frame */
9227     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9228
9229   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9230   {
9231     MovDelay[x][y]--;
9232
9233     DrawLevelElementAnimation(x, y, Feld[x][y]);
9234
9235     if (MovDelay[x][y] != 0)
9236     {
9237       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9238                                            10 - MovDelay[x][y]);
9239
9240       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9241     }
9242   }
9243 }
9244
9245 void MauerWaechst(int x, int y)
9246 {
9247   int delay = 6;
9248
9249   if (!MovDelay[x][y])          /* next animation frame */
9250     MovDelay[x][y] = 3 * delay;
9251
9252   if (MovDelay[x][y])           /* wait some time before next frame */
9253   {
9254     MovDelay[x][y]--;
9255
9256     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9257     {
9258       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9259       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9260
9261       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9262     }
9263
9264     if (!MovDelay[x][y])
9265     {
9266       if (MovDir[x][y] == MV_LEFT)
9267       {
9268         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9269           TEST_DrawLevelField(x - 1, y);
9270       }
9271       else if (MovDir[x][y] == MV_RIGHT)
9272       {
9273         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9274           TEST_DrawLevelField(x + 1, y);
9275       }
9276       else if (MovDir[x][y] == MV_UP)
9277       {
9278         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9279           TEST_DrawLevelField(x, y - 1);
9280       }
9281       else
9282       {
9283         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9284           TEST_DrawLevelField(x, y + 1);
9285       }
9286
9287       Feld[x][y] = Store[x][y];
9288       Store[x][y] = 0;
9289       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9290       TEST_DrawLevelField(x, y);
9291     }
9292   }
9293 }
9294
9295 void MauerAbleger(int ax, int ay)
9296 {
9297   int element = Feld[ax][ay];
9298   int graphic = el2img(element);
9299   boolean oben_frei = FALSE, unten_frei = FALSE;
9300   boolean links_frei = FALSE, rechts_frei = FALSE;
9301   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9302   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9303   boolean new_wall = FALSE;
9304
9305   if (IS_ANIMATED(graphic))
9306     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9307
9308   if (!MovDelay[ax][ay])        /* start building new wall */
9309     MovDelay[ax][ay] = 6;
9310
9311   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9312   {
9313     MovDelay[ax][ay]--;
9314     if (MovDelay[ax][ay])
9315       return;
9316   }
9317
9318   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9319     oben_frei = TRUE;
9320   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9321     unten_frei = TRUE;
9322   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9323     links_frei = TRUE;
9324   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9325     rechts_frei = TRUE;
9326
9327   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9328       element == EL_EXPANDABLE_WALL_ANY)
9329   {
9330     if (oben_frei)
9331     {
9332       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9333       Store[ax][ay-1] = element;
9334       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9335       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9336         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9337                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9338       new_wall = TRUE;
9339     }
9340     if (unten_frei)
9341     {
9342       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9343       Store[ax][ay+1] = element;
9344       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9345       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9346         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9347                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9348       new_wall = TRUE;
9349     }
9350   }
9351
9352   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9353       element == EL_EXPANDABLE_WALL_ANY ||
9354       element == EL_EXPANDABLE_WALL ||
9355       element == EL_BD_EXPANDABLE_WALL)
9356   {
9357     if (links_frei)
9358     {
9359       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9360       Store[ax-1][ay] = element;
9361       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9362       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9363         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9364                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9365       new_wall = TRUE;
9366     }
9367
9368     if (rechts_frei)
9369     {
9370       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9371       Store[ax+1][ay] = element;
9372       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9373       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9374         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9375                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9376       new_wall = TRUE;
9377     }
9378   }
9379
9380   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9381     TEST_DrawLevelField(ax, ay);
9382
9383   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9384     oben_massiv = TRUE;
9385   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9386     unten_massiv = TRUE;
9387   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9388     links_massiv = TRUE;
9389   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9390     rechts_massiv = TRUE;
9391
9392   if (((oben_massiv && unten_massiv) ||
9393        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9394        element == EL_EXPANDABLE_WALL) &&
9395       ((links_massiv && rechts_massiv) ||
9396        element == EL_EXPANDABLE_WALL_VERTICAL))
9397     Feld[ax][ay] = EL_WALL;
9398
9399   if (new_wall)
9400     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9401 }
9402
9403 void MauerAblegerStahl(int ax, int ay)
9404 {
9405   int element = Feld[ax][ay];
9406   int graphic = el2img(element);
9407   boolean oben_frei = FALSE, unten_frei = FALSE;
9408   boolean links_frei = FALSE, rechts_frei = FALSE;
9409   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9410   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9411   boolean new_wall = FALSE;
9412
9413   if (IS_ANIMATED(graphic))
9414     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9415
9416   if (!MovDelay[ax][ay])        /* start building new wall */
9417     MovDelay[ax][ay] = 6;
9418
9419   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9420   {
9421     MovDelay[ax][ay]--;
9422     if (MovDelay[ax][ay])
9423       return;
9424   }
9425
9426   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9427     oben_frei = TRUE;
9428   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9429     unten_frei = TRUE;
9430   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9431     links_frei = TRUE;
9432   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9433     rechts_frei = TRUE;
9434
9435   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9436       element == EL_EXPANDABLE_STEELWALL_ANY)
9437   {
9438     if (oben_frei)
9439     {
9440       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9441       Store[ax][ay-1] = element;
9442       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9443       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9444         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9445                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9446       new_wall = TRUE;
9447     }
9448     if (unten_frei)
9449     {
9450       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9451       Store[ax][ay+1] = element;
9452       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9453       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9454         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9455                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9456       new_wall = TRUE;
9457     }
9458   }
9459
9460   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9461       element == EL_EXPANDABLE_STEELWALL_ANY)
9462   {
9463     if (links_frei)
9464     {
9465       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9466       Store[ax-1][ay] = element;
9467       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9468       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9469         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9470                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9471       new_wall = TRUE;
9472     }
9473
9474     if (rechts_frei)
9475     {
9476       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9477       Store[ax+1][ay] = element;
9478       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9479       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9480         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9481                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9482       new_wall = TRUE;
9483     }
9484   }
9485
9486   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9487     oben_massiv = TRUE;
9488   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9489     unten_massiv = TRUE;
9490   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9491     links_massiv = TRUE;
9492   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9493     rechts_massiv = TRUE;
9494
9495   if (((oben_massiv && unten_massiv) ||
9496        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9497       ((links_massiv && rechts_massiv) ||
9498        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9499     Feld[ax][ay] = EL_STEELWALL;
9500
9501   if (new_wall)
9502     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9503 }
9504
9505 void CheckForDragon(int x, int y)
9506 {
9507   int i, j;
9508   boolean dragon_found = FALSE;
9509   static int xy[4][2] =
9510   {
9511     { 0, -1 },
9512     { -1, 0 },
9513     { +1, 0 },
9514     { 0, +1 }
9515   };
9516
9517   for (i = 0; i < NUM_DIRECTIONS; i++)
9518   {
9519     for (j = 0; j < 4; j++)
9520     {
9521       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9522
9523       if (IN_LEV_FIELD(xx, yy) &&
9524           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9525       {
9526         if (Feld[xx][yy] == EL_DRAGON)
9527           dragon_found = TRUE;
9528       }
9529       else
9530         break;
9531     }
9532   }
9533
9534   if (!dragon_found)
9535   {
9536     for (i = 0; i < NUM_DIRECTIONS; i++)
9537     {
9538       for (j = 0; j < 3; j++)
9539       {
9540         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9541   
9542         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9543         {
9544           Feld[xx][yy] = EL_EMPTY;
9545           TEST_DrawLevelField(xx, yy);
9546         }
9547         else
9548           break;
9549       }
9550     }
9551   }
9552 }
9553
9554 static void InitBuggyBase(int x, int y)
9555 {
9556   int element = Feld[x][y];
9557   int activating_delay = FRAMES_PER_SECOND / 4;
9558
9559   ChangeDelay[x][y] =
9560     (element == EL_SP_BUGGY_BASE ?
9561      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9562      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9563      activating_delay :
9564      element == EL_SP_BUGGY_BASE_ACTIVE ?
9565      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9566 }
9567
9568 static void WarnBuggyBase(int x, int y)
9569 {
9570   int i;
9571   static int xy[4][2] =
9572   {
9573     { 0, -1 },
9574     { -1, 0 },
9575     { +1, 0 },
9576     { 0, +1 }
9577   };
9578
9579   for (i = 0; i < NUM_DIRECTIONS; i++)
9580   {
9581     int xx = x + xy[i][0];
9582     int yy = y + xy[i][1];
9583
9584     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9585     {
9586       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9587
9588       break;
9589     }
9590   }
9591 }
9592
9593 static void InitTrap(int x, int y)
9594 {
9595   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9596 }
9597
9598 static void ActivateTrap(int x, int y)
9599 {
9600   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9601 }
9602
9603 static void ChangeActiveTrap(int x, int y)
9604 {
9605   int graphic = IMG_TRAP_ACTIVE;
9606
9607   /* if new animation frame was drawn, correct crumbled sand border */
9608   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9609     TEST_DrawLevelFieldCrumbled(x, y);
9610 }
9611
9612 static int getSpecialActionElement(int element, int number, int base_element)
9613 {
9614   return (element != EL_EMPTY ? element :
9615           number != -1 ? base_element + number - 1 :
9616           EL_EMPTY);
9617 }
9618
9619 static int getModifiedActionNumber(int value_old, int operator, int operand,
9620                                    int value_min, int value_max)
9621 {
9622   int value_new = (operator == CA_MODE_SET      ? operand :
9623                    operator == CA_MODE_ADD      ? value_old + operand :
9624                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9625                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9626                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9627                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9628                    value_old);
9629
9630   return (value_new < value_min ? value_min :
9631           value_new > value_max ? value_max :
9632           value_new);
9633 }
9634
9635 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9636 {
9637   struct ElementInfo *ei = &element_info[element];
9638   struct ElementChangeInfo *change = &ei->change_page[page];
9639   int target_element = change->target_element;
9640   int action_type = change->action_type;
9641   int action_mode = change->action_mode;
9642   int action_arg = change->action_arg;
9643   int action_element = change->action_element;
9644   int i;
9645
9646   if (!change->has_action)
9647     return;
9648
9649   /* ---------- determine action paramater values -------------------------- */
9650
9651   int level_time_value =
9652     (level.time > 0 ? TimeLeft :
9653      TimePlayed);
9654
9655   int action_arg_element_raw =
9656     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9657      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9658      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9659      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9660      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9661      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9662      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9663      EL_EMPTY);
9664   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9665
9666   int action_arg_direction =
9667     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9668      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9669      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9670      change->actual_trigger_side :
9671      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9672      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9673      MV_NONE);
9674
9675   int action_arg_number_min =
9676     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9677      CA_ARG_MIN);
9678
9679   int action_arg_number_max =
9680     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9681      action_type == CA_SET_LEVEL_GEMS ? 999 :
9682      action_type == CA_SET_LEVEL_TIME ? 9999 :
9683      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9684      action_type == CA_SET_CE_VALUE ? 9999 :
9685      action_type == CA_SET_CE_SCORE ? 9999 :
9686      CA_ARG_MAX);
9687
9688   int action_arg_number_reset =
9689     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9690      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9691      action_type == CA_SET_LEVEL_TIME ? level.time :
9692      action_type == CA_SET_LEVEL_SCORE ? 0 :
9693      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9694      action_type == CA_SET_CE_SCORE ? 0 :
9695      0);
9696
9697   int action_arg_number =
9698     (action_arg <= CA_ARG_MAX ? action_arg :
9699      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9700      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9701      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9702      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9703      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9704      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9705      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9706      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9707      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9708      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9709      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9710      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9711      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9712      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9713      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9714      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9715      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9716      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9717      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9718      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9719      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9720      -1);
9721
9722   int action_arg_number_old =
9723     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9724      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9725      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9726      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9727      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9728      0);
9729
9730   int action_arg_number_new =
9731     getModifiedActionNumber(action_arg_number_old,
9732                             action_mode, action_arg_number,
9733                             action_arg_number_min, action_arg_number_max);
9734
9735   int trigger_player_bits =
9736     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9737      change->actual_trigger_player_bits : change->trigger_player);
9738
9739   int action_arg_player_bits =
9740     (action_arg >= CA_ARG_PLAYER_1 &&
9741      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9742      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9743      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9744      PLAYER_BITS_ANY);
9745
9746   /* ---------- execute action  -------------------------------------------- */
9747
9748   switch (action_type)
9749   {
9750     case CA_NO_ACTION:
9751     {
9752       return;
9753     }
9754
9755     /* ---------- level actions  ------------------------------------------- */
9756
9757     case CA_RESTART_LEVEL:
9758     {
9759       game.restart_level = TRUE;
9760
9761       break;
9762     }
9763
9764     case CA_SHOW_ENVELOPE:
9765     {
9766       int element = getSpecialActionElement(action_arg_element,
9767                                             action_arg_number, EL_ENVELOPE_1);
9768
9769       if (IS_ENVELOPE(element))
9770         local_player->show_envelope = element;
9771
9772       break;
9773     }
9774
9775     case CA_SET_LEVEL_TIME:
9776     {
9777       if (level.time > 0)       /* only modify limited time value */
9778       {
9779         TimeLeft = action_arg_number_new;
9780
9781         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9782
9783         DisplayGameControlValues();
9784
9785         if (!TimeLeft && setup.time_limit)
9786           for (i = 0; i < MAX_PLAYERS; i++)
9787             KillPlayer(&stored_player[i]);
9788       }
9789
9790       break;
9791     }
9792
9793     case CA_SET_LEVEL_SCORE:
9794     {
9795       local_player->score = action_arg_number_new;
9796
9797       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9798
9799       DisplayGameControlValues();
9800
9801       break;
9802     }
9803
9804     case CA_SET_LEVEL_GEMS:
9805     {
9806       local_player->gems_still_needed = action_arg_number_new;
9807
9808       game.snapshot.collected_item = TRUE;
9809
9810       game_panel_controls[GAME_PANEL_GEMS].value =
9811         local_player->gems_still_needed;
9812
9813       DisplayGameControlValues();
9814
9815       break;
9816     }
9817
9818     case CA_SET_LEVEL_WIND:
9819     {
9820       game.wind_direction = action_arg_direction;
9821
9822       break;
9823     }
9824
9825     case CA_SET_LEVEL_RANDOM_SEED:
9826     {
9827       /* ensure that setting a new random seed while playing is predictable */
9828       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9829
9830       break;
9831     }
9832
9833     /* ---------- player actions  ------------------------------------------ */
9834
9835     case CA_MOVE_PLAYER:
9836     {
9837       /* automatically move to the next field in specified direction */
9838       for (i = 0; i < MAX_PLAYERS; i++)
9839         if (trigger_player_bits & (1 << i))
9840           stored_player[i].programmed_action = action_arg_direction;
9841
9842       break;
9843     }
9844
9845     case CA_EXIT_PLAYER:
9846     {
9847       for (i = 0; i < MAX_PLAYERS; i++)
9848         if (action_arg_player_bits & (1 << i))
9849           PlayerWins(&stored_player[i]);
9850
9851       break;
9852     }
9853
9854     case CA_KILL_PLAYER:
9855     {
9856       for (i = 0; i < MAX_PLAYERS; i++)
9857         if (action_arg_player_bits & (1 << i))
9858           KillPlayer(&stored_player[i]);
9859
9860       break;
9861     }
9862
9863     case CA_SET_PLAYER_KEYS:
9864     {
9865       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9866       int element = getSpecialActionElement(action_arg_element,
9867                                             action_arg_number, EL_KEY_1);
9868
9869       if (IS_KEY(element))
9870       {
9871         for (i = 0; i < MAX_PLAYERS; i++)
9872         {
9873           if (trigger_player_bits & (1 << i))
9874           {
9875             stored_player[i].key[KEY_NR(element)] = key_state;
9876
9877             DrawGameDoorValues();
9878           }
9879         }
9880       }
9881
9882       break;
9883     }
9884
9885     case CA_SET_PLAYER_SPEED:
9886     {
9887       for (i = 0; i < MAX_PLAYERS; i++)
9888       {
9889         if (trigger_player_bits & (1 << i))
9890         {
9891           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9892
9893           if (action_arg == CA_ARG_SPEED_FASTER &&
9894               stored_player[i].cannot_move)
9895           {
9896             action_arg_number = STEPSIZE_VERY_SLOW;
9897           }
9898           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9899                    action_arg == CA_ARG_SPEED_FASTER)
9900           {
9901             action_arg_number = 2;
9902             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9903                            CA_MODE_MULTIPLY);
9904           }
9905           else if (action_arg == CA_ARG_NUMBER_RESET)
9906           {
9907             action_arg_number = level.initial_player_stepsize[i];
9908           }
9909
9910           move_stepsize =
9911             getModifiedActionNumber(move_stepsize,
9912                                     action_mode,
9913                                     action_arg_number,
9914                                     action_arg_number_min,
9915                                     action_arg_number_max);
9916
9917           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9918         }
9919       }
9920
9921       break;
9922     }
9923
9924     case CA_SET_PLAYER_SHIELD:
9925     {
9926       for (i = 0; i < MAX_PLAYERS; i++)
9927       {
9928         if (trigger_player_bits & (1 << i))
9929         {
9930           if (action_arg == CA_ARG_SHIELD_OFF)
9931           {
9932             stored_player[i].shield_normal_time_left = 0;
9933             stored_player[i].shield_deadly_time_left = 0;
9934           }
9935           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9936           {
9937             stored_player[i].shield_normal_time_left = 999999;
9938           }
9939           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9940           {
9941             stored_player[i].shield_normal_time_left = 999999;
9942             stored_player[i].shield_deadly_time_left = 999999;
9943           }
9944         }
9945       }
9946
9947       break;
9948     }
9949
9950     case CA_SET_PLAYER_GRAVITY:
9951     {
9952       for (i = 0; i < MAX_PLAYERS; i++)
9953       {
9954         if (trigger_player_bits & (1 << i))
9955         {
9956           stored_player[i].gravity =
9957             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9958              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9959              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9960              stored_player[i].gravity);
9961         }
9962       }
9963
9964       break;
9965     }
9966
9967     case CA_SET_PLAYER_ARTWORK:
9968     {
9969       for (i = 0; i < MAX_PLAYERS; i++)
9970       {
9971         if (trigger_player_bits & (1 << i))
9972         {
9973           int artwork_element = action_arg_element;
9974
9975           if (action_arg == CA_ARG_ELEMENT_RESET)
9976             artwork_element =
9977               (level.use_artwork_element[i] ? level.artwork_element[i] :
9978                stored_player[i].element_nr);
9979
9980           if (stored_player[i].artwork_element != artwork_element)
9981             stored_player[i].Frame = 0;
9982
9983           stored_player[i].artwork_element = artwork_element;
9984
9985           SetPlayerWaiting(&stored_player[i], FALSE);
9986
9987           /* set number of special actions for bored and sleeping animation */
9988           stored_player[i].num_special_action_bored =
9989             get_num_special_action(artwork_element,
9990                                    ACTION_BORING_1, ACTION_BORING_LAST);
9991           stored_player[i].num_special_action_sleeping =
9992             get_num_special_action(artwork_element,
9993                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9994         }
9995       }
9996
9997       break;
9998     }
9999
10000     case CA_SET_PLAYER_INVENTORY:
10001     {
10002       for (i = 0; i < MAX_PLAYERS; i++)
10003       {
10004         struct PlayerInfo *player = &stored_player[i];
10005         int j, k;
10006
10007         if (trigger_player_bits & (1 << i))
10008         {
10009           int inventory_element = action_arg_element;
10010
10011           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10012               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10013               action_arg == CA_ARG_ELEMENT_ACTION)
10014           {
10015             int element = inventory_element;
10016             int collect_count = element_info[element].collect_count_initial;
10017
10018             if (!IS_CUSTOM_ELEMENT(element))
10019               collect_count = 1;
10020
10021             if (collect_count == 0)
10022               player->inventory_infinite_element = element;
10023             else
10024               for (k = 0; k < collect_count; k++)
10025                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10026                   player->inventory_element[player->inventory_size++] =
10027                     element;
10028           }
10029           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10030                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10031                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10032           {
10033             if (player->inventory_infinite_element != EL_UNDEFINED &&
10034                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10035                                      action_arg_element_raw))
10036               player->inventory_infinite_element = EL_UNDEFINED;
10037
10038             for (k = 0, j = 0; j < player->inventory_size; j++)
10039             {
10040               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10041                                         action_arg_element_raw))
10042                 player->inventory_element[k++] = player->inventory_element[j];
10043             }
10044
10045             player->inventory_size = k;
10046           }
10047           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10048           {
10049             if (player->inventory_size > 0)
10050             {
10051               for (j = 0; j < player->inventory_size - 1; j++)
10052                 player->inventory_element[j] = player->inventory_element[j + 1];
10053
10054               player->inventory_size--;
10055             }
10056           }
10057           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10058           {
10059             if (player->inventory_size > 0)
10060               player->inventory_size--;
10061           }
10062           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10063           {
10064             player->inventory_infinite_element = EL_UNDEFINED;
10065             player->inventory_size = 0;
10066           }
10067           else if (action_arg == CA_ARG_INVENTORY_RESET)
10068           {
10069             player->inventory_infinite_element = EL_UNDEFINED;
10070             player->inventory_size = 0;
10071
10072             if (level.use_initial_inventory[i])
10073             {
10074               for (j = 0; j < level.initial_inventory_size[i]; j++)
10075               {
10076                 int element = level.initial_inventory_content[i][j];
10077                 int collect_count = element_info[element].collect_count_initial;
10078
10079                 if (!IS_CUSTOM_ELEMENT(element))
10080                   collect_count = 1;
10081
10082                 if (collect_count == 0)
10083                   player->inventory_infinite_element = element;
10084                 else
10085                   for (k = 0; k < collect_count; k++)
10086                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10087                       player->inventory_element[player->inventory_size++] =
10088                         element;
10089               }
10090             }
10091           }
10092         }
10093       }
10094
10095       break;
10096     }
10097
10098     /* ---------- CE actions  ---------------------------------------------- */
10099
10100     case CA_SET_CE_VALUE:
10101     {
10102       int last_ce_value = CustomValue[x][y];
10103
10104       CustomValue[x][y] = action_arg_number_new;
10105
10106       if (CustomValue[x][y] != last_ce_value)
10107       {
10108         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10109         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10110
10111         if (CustomValue[x][y] == 0)
10112         {
10113           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10114           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10115         }
10116       }
10117
10118       break;
10119     }
10120
10121     case CA_SET_CE_SCORE:
10122     {
10123       int last_ce_score = ei->collect_score;
10124
10125       ei->collect_score = action_arg_number_new;
10126
10127       if (ei->collect_score != last_ce_score)
10128       {
10129         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10130         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10131
10132         if (ei->collect_score == 0)
10133         {
10134           int xx, yy;
10135
10136           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10137           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10138
10139           /*
10140             This is a very special case that seems to be a mixture between
10141             CheckElementChange() and CheckTriggeredElementChange(): while
10142             the first one only affects single elements that are triggered
10143             directly, the second one affects multiple elements in the playfield
10144             that are triggered indirectly by another element. This is a third
10145             case: Changing the CE score always affects multiple identical CEs,
10146             so every affected CE must be checked, not only the single CE for
10147             which the CE score was changed in the first place (as every instance
10148             of that CE shares the same CE score, and therefore also can change)!
10149           */
10150           SCAN_PLAYFIELD(xx, yy)
10151           {
10152             if (Feld[xx][yy] == element)
10153               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10154                                  CE_SCORE_GETS_ZERO);
10155           }
10156         }
10157       }
10158
10159       break;
10160     }
10161
10162     case CA_SET_CE_ARTWORK:
10163     {
10164       int artwork_element = action_arg_element;
10165       boolean reset_frame = FALSE;
10166       int xx, yy;
10167
10168       if (action_arg == CA_ARG_ELEMENT_RESET)
10169         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10170                            element);
10171
10172       if (ei->gfx_element != artwork_element)
10173         reset_frame = TRUE;
10174
10175       ei->gfx_element = artwork_element;
10176
10177       SCAN_PLAYFIELD(xx, yy)
10178       {
10179         if (Feld[xx][yy] == element)
10180         {
10181           if (reset_frame)
10182           {
10183             ResetGfxAnimation(xx, yy);
10184             ResetRandomAnimationValue(xx, yy);
10185           }
10186
10187           TEST_DrawLevelField(xx, yy);
10188         }
10189       }
10190
10191       break;
10192     }
10193
10194     /* ---------- engine actions  ------------------------------------------ */
10195
10196     case CA_SET_ENGINE_SCAN_MODE:
10197     {
10198       InitPlayfieldScanMode(action_arg);
10199
10200       break;
10201     }
10202
10203     default:
10204       break;
10205   }
10206 }
10207
10208 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10209 {
10210   int old_element = Feld[x][y];
10211   int new_element = GetElementFromGroupElement(element);
10212   int previous_move_direction = MovDir[x][y];
10213   int last_ce_value = CustomValue[x][y];
10214   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10215   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10216   boolean add_player_onto_element = (new_element_is_player &&
10217                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10218                                      IS_WALKABLE(old_element));
10219
10220   if (!add_player_onto_element)
10221   {
10222     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10223       RemoveMovingField(x, y);
10224     else
10225       RemoveField(x, y);
10226
10227     Feld[x][y] = new_element;
10228
10229     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10230       MovDir[x][y] = previous_move_direction;
10231
10232     if (element_info[new_element].use_last_ce_value)
10233       CustomValue[x][y] = last_ce_value;
10234
10235     InitField_WithBug1(x, y, FALSE);
10236
10237     new_element = Feld[x][y];   /* element may have changed */
10238
10239     ResetGfxAnimation(x, y);
10240     ResetRandomAnimationValue(x, y);
10241
10242     TEST_DrawLevelField(x, y);
10243
10244     if (GFX_CRUMBLED(new_element))
10245       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10246   }
10247
10248   /* check if element under the player changes from accessible to unaccessible
10249      (needed for special case of dropping element which then changes) */
10250   /* (must be checked after creating new element for walkable group elements) */
10251   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10252       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10253   {
10254     Bang(x, y);
10255
10256     return;
10257   }
10258
10259   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10260   if (new_element_is_player)
10261     RelocatePlayer(x, y, new_element);
10262
10263   if (is_change)
10264     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10265
10266   TestIfBadThingTouchesPlayer(x, y);
10267   TestIfPlayerTouchesCustomElement(x, y);
10268   TestIfElementTouchesCustomElement(x, y);
10269 }
10270
10271 static void CreateField(int x, int y, int element)
10272 {
10273   CreateFieldExt(x, y, element, FALSE);
10274 }
10275
10276 static void CreateElementFromChange(int x, int y, int element)
10277 {
10278   element = GET_VALID_RUNTIME_ELEMENT(element);
10279
10280   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10281   {
10282     int old_element = Feld[x][y];
10283
10284     /* prevent changed element from moving in same engine frame
10285        unless both old and new element can either fall or move */
10286     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10287         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10288       Stop[x][y] = TRUE;
10289   }
10290
10291   CreateFieldExt(x, y, element, TRUE);
10292 }
10293
10294 static boolean ChangeElement(int x, int y, int element, int page)
10295 {
10296   struct ElementInfo *ei = &element_info[element];
10297   struct ElementChangeInfo *change = &ei->change_page[page];
10298   int ce_value = CustomValue[x][y];
10299   int ce_score = ei->collect_score;
10300   int target_element;
10301   int old_element = Feld[x][y];
10302
10303   /* always use default change event to prevent running into a loop */
10304   if (ChangeEvent[x][y] == -1)
10305     ChangeEvent[x][y] = CE_DELAY;
10306
10307   if (ChangeEvent[x][y] == CE_DELAY)
10308   {
10309     /* reset actual trigger element, trigger player and action element */
10310     change->actual_trigger_element = EL_EMPTY;
10311     change->actual_trigger_player = EL_EMPTY;
10312     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10313     change->actual_trigger_side = CH_SIDE_NONE;
10314     change->actual_trigger_ce_value = 0;
10315     change->actual_trigger_ce_score = 0;
10316   }
10317
10318   /* do not change elements more than a specified maximum number of changes */
10319   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10320     return FALSE;
10321
10322   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10323
10324   if (change->explode)
10325   {
10326     Bang(x, y);
10327
10328     return TRUE;
10329   }
10330
10331   if (change->use_target_content)
10332   {
10333     boolean complete_replace = TRUE;
10334     boolean can_replace[3][3];
10335     int xx, yy;
10336
10337     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10338     {
10339       boolean is_empty;
10340       boolean is_walkable;
10341       boolean is_diggable;
10342       boolean is_collectible;
10343       boolean is_removable;
10344       boolean is_destructible;
10345       int ex = x + xx - 1;
10346       int ey = y + yy - 1;
10347       int content_element = change->target_content.e[xx][yy];
10348       int e;
10349
10350       can_replace[xx][yy] = TRUE;
10351
10352       if (ex == x && ey == y)   /* do not check changing element itself */
10353         continue;
10354
10355       if (content_element == EL_EMPTY_SPACE)
10356       {
10357         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10358
10359         continue;
10360       }
10361
10362       if (!IN_LEV_FIELD(ex, ey))
10363       {
10364         can_replace[xx][yy] = FALSE;
10365         complete_replace = FALSE;
10366
10367         continue;
10368       }
10369
10370       e = Feld[ex][ey];
10371
10372       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10373         e = MovingOrBlocked2Element(ex, ey);
10374
10375       is_empty = (IS_FREE(ex, ey) ||
10376                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10377
10378       is_walkable     = (is_empty || IS_WALKABLE(e));
10379       is_diggable     = (is_empty || IS_DIGGABLE(e));
10380       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10381       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10382       is_removable    = (is_diggable || is_collectible);
10383
10384       can_replace[xx][yy] =
10385         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10386           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10387           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10388           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10389           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10390           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10391          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10392
10393       if (!can_replace[xx][yy])
10394         complete_replace = FALSE;
10395     }
10396
10397     if (!change->only_if_complete || complete_replace)
10398     {
10399       boolean something_has_changed = FALSE;
10400
10401       if (change->only_if_complete && change->use_random_replace &&
10402           RND(100) < change->random_percentage)
10403         return FALSE;
10404
10405       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10406       {
10407         int ex = x + xx - 1;
10408         int ey = y + yy - 1;
10409         int content_element;
10410
10411         if (can_replace[xx][yy] && (!change->use_random_replace ||
10412                                     RND(100) < change->random_percentage))
10413         {
10414           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10415             RemoveMovingField(ex, ey);
10416
10417           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10418
10419           content_element = change->target_content.e[xx][yy];
10420           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10421                                               ce_value, ce_score);
10422
10423           CreateElementFromChange(ex, ey, target_element);
10424
10425           something_has_changed = TRUE;
10426
10427           /* for symmetry reasons, freeze newly created border elements */
10428           if (ex != x || ey != y)
10429             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10430         }
10431       }
10432
10433       if (something_has_changed)
10434       {
10435         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10436         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10437       }
10438     }
10439   }
10440   else
10441   {
10442     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10443                                         ce_value, ce_score);
10444
10445     if (element == EL_DIAGONAL_GROWING ||
10446         element == EL_DIAGONAL_SHRINKING)
10447     {
10448       target_element = Store[x][y];
10449
10450       Store[x][y] = EL_EMPTY;
10451     }
10452
10453     CreateElementFromChange(x, y, target_element);
10454
10455     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10456     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10457   }
10458
10459   /* this uses direct change before indirect change */
10460   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10461
10462   return TRUE;
10463 }
10464
10465 static void HandleElementChange(int x, int y, int page)
10466 {
10467   int element = MovingOrBlocked2Element(x, y);
10468   struct ElementInfo *ei = &element_info[element];
10469   struct ElementChangeInfo *change = &ei->change_page[page];
10470   boolean handle_action_before_change = FALSE;
10471
10472 #ifdef DEBUG
10473   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10474       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10475   {
10476     printf("\n\n");
10477     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10478            x, y, element, element_info[element].token_name);
10479     printf("HandleElementChange(): This should never happen!\n");
10480     printf("\n\n");
10481   }
10482 #endif
10483
10484   /* this can happen with classic bombs on walkable, changing elements */
10485   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10486   {
10487     return;
10488   }
10489
10490   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10491   {
10492     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10493
10494     if (change->can_change)
10495     {
10496       /* !!! not clear why graphic animation should be reset at all here !!! */
10497       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10498       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10499
10500       /*
10501         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10502
10503         When using an animation frame delay of 1 (this only happens with
10504         "sp_zonk.moving.left/right" in the classic graphics), the default
10505         (non-moving) animation shows wrong animation frames (while the
10506         moving animation, like "sp_zonk.moving.left/right", is correct,
10507         so this graphical bug never shows up with the classic graphics).
10508         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10509         be drawn instead of the correct frames 0,1,2,3. This is caused by
10510         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10511         an element change: First when the change delay ("ChangeDelay[][]")
10512         counter has reached zero after decrementing, then a second time in
10513         the next frame (after "GfxFrame[][]" was already incremented) when
10514         "ChangeDelay[][]" is reset to the initial delay value again.
10515
10516         This causes frame 0 to be drawn twice, while the last frame won't
10517         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10518
10519         As some animations may already be cleverly designed around this bug
10520         (at least the "Snake Bite" snake tail animation does this), it cannot
10521         simply be fixed here without breaking such existing animations.
10522         Unfortunately, it cannot easily be detected if a graphics set was
10523         designed "before" or "after" the bug was fixed. As a workaround,
10524         a new graphics set option "game.graphics_engine_version" was added
10525         to be able to specify the game's major release version for which the
10526         graphics set was designed, which can then be used to decide if the
10527         bugfix should be used (version 4 and above) or not (version 3 or
10528         below, or if no version was specified at all, as with old sets).
10529
10530         (The wrong/fixed animation frames can be tested with the test level set
10531         "test_gfxframe" and level "000", which contains a specially prepared
10532         custom element at level position (x/y) == (11/9) which uses the zonk
10533         animation mentioned above. Using "game.graphics_engine_version: 4"
10534         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10535         This can also be seen from the debug output for this test element.)
10536       */
10537
10538       /* when a custom element is about to change (for example by change delay),
10539          do not reset graphic animation when the custom element is moving */
10540       if (game.graphics_engine_version < 4 &&
10541           !IS_MOVING(x, y))
10542       {
10543         ResetGfxAnimation(x, y);
10544         ResetRandomAnimationValue(x, y);
10545       }
10546
10547       if (change->pre_change_function)
10548         change->pre_change_function(x, y);
10549     }
10550   }
10551
10552   ChangeDelay[x][y]--;
10553
10554   if (ChangeDelay[x][y] != 0)           /* continue element change */
10555   {
10556     if (change->can_change)
10557     {
10558       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10559
10560       if (IS_ANIMATED(graphic))
10561         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10562
10563       if (change->change_function)
10564         change->change_function(x, y);
10565     }
10566   }
10567   else                                  /* finish element change */
10568   {
10569     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10570     {
10571       page = ChangePage[x][y];
10572       ChangePage[x][y] = -1;
10573
10574       change = &ei->change_page[page];
10575     }
10576
10577     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10578     {
10579       ChangeDelay[x][y] = 1;            /* try change after next move step */
10580       ChangePage[x][y] = page;          /* remember page to use for change */
10581
10582       return;
10583     }
10584
10585     /* special case: set new level random seed before changing element */
10586     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10587       handle_action_before_change = TRUE;
10588
10589     if (change->has_action && handle_action_before_change)
10590       ExecuteCustomElementAction(x, y, element, page);
10591
10592     if (change->can_change)
10593     {
10594       if (ChangeElement(x, y, element, page))
10595       {
10596         if (change->post_change_function)
10597           change->post_change_function(x, y);
10598       }
10599     }
10600
10601     if (change->has_action && !handle_action_before_change)
10602       ExecuteCustomElementAction(x, y, element, page);
10603   }
10604 }
10605
10606 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10607                                               int trigger_element,
10608                                               int trigger_event,
10609                                               int trigger_player,
10610                                               int trigger_side,
10611                                               int trigger_page)
10612 {
10613   boolean change_done_any = FALSE;
10614   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10615   int i;
10616
10617   if (!(trigger_events[trigger_element][trigger_event]))
10618     return FALSE;
10619
10620   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10621
10622   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10623   {
10624     int element = EL_CUSTOM_START + i;
10625     boolean change_done = FALSE;
10626     int p;
10627
10628     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10629         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10630       continue;
10631
10632     for (p = 0; p < element_info[element].num_change_pages; p++)
10633     {
10634       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10635
10636       if (change->can_change_or_has_action &&
10637           change->has_event[trigger_event] &&
10638           change->trigger_side & trigger_side &&
10639           change->trigger_player & trigger_player &&
10640           change->trigger_page & trigger_page_bits &&
10641           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10642       {
10643         change->actual_trigger_element = trigger_element;
10644         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10645         change->actual_trigger_player_bits = trigger_player;
10646         change->actual_trigger_side = trigger_side;
10647         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10648         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10649
10650         if ((change->can_change && !change_done) || change->has_action)
10651         {
10652           int x, y;
10653
10654           SCAN_PLAYFIELD(x, y)
10655           {
10656             if (Feld[x][y] == element)
10657             {
10658               if (change->can_change && !change_done)
10659               {
10660                 /* if element already changed in this frame, not only prevent
10661                    another element change (checked in ChangeElement()), but
10662                    also prevent additional element actions for this element */
10663
10664                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10665                     !level.use_action_after_change_bug)
10666                   continue;
10667
10668                 ChangeDelay[x][y] = 1;
10669                 ChangeEvent[x][y] = trigger_event;
10670
10671                 HandleElementChange(x, y, p);
10672               }
10673               else if (change->has_action)
10674               {
10675                 /* if element already changed in this frame, not only prevent
10676                    another element change (checked in ChangeElement()), but
10677                    also prevent additional element actions for this element */
10678
10679                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10680                     !level.use_action_after_change_bug)
10681                   continue;
10682
10683                 ExecuteCustomElementAction(x, y, element, p);
10684                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10685               }
10686             }
10687           }
10688
10689           if (change->can_change)
10690           {
10691             change_done = TRUE;
10692             change_done_any = TRUE;
10693           }
10694         }
10695       }
10696     }
10697   }
10698
10699   RECURSION_LOOP_DETECTION_END();
10700
10701   return change_done_any;
10702 }
10703
10704 static boolean CheckElementChangeExt(int x, int y,
10705                                      int element,
10706                                      int trigger_element,
10707                                      int trigger_event,
10708                                      int trigger_player,
10709                                      int trigger_side)
10710 {
10711   boolean change_done = FALSE;
10712   int p;
10713
10714   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10715       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10716     return FALSE;
10717
10718   if (Feld[x][y] == EL_BLOCKED)
10719   {
10720     Blocked2Moving(x, y, &x, &y);
10721     element = Feld[x][y];
10722   }
10723
10724   /* check if element has already changed or is about to change after moving */
10725   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10726        Feld[x][y] != element) ||
10727
10728       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10729        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10730         ChangePage[x][y] != -1)))
10731     return FALSE;
10732
10733   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10734
10735   for (p = 0; p < element_info[element].num_change_pages; p++)
10736   {
10737     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10738
10739     /* check trigger element for all events where the element that is checked
10740        for changing interacts with a directly adjacent element -- this is
10741        different to element changes that affect other elements to change on the
10742        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10743     boolean check_trigger_element =
10744       (trigger_event == CE_TOUCHING_X ||
10745        trigger_event == CE_HITTING_X ||
10746        trigger_event == CE_HIT_BY_X ||
10747        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10748
10749     if (change->can_change_or_has_action &&
10750         change->has_event[trigger_event] &&
10751         change->trigger_side & trigger_side &&
10752         change->trigger_player & trigger_player &&
10753         (!check_trigger_element ||
10754          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10755     {
10756       change->actual_trigger_element = trigger_element;
10757       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10758       change->actual_trigger_player_bits = trigger_player;
10759       change->actual_trigger_side = trigger_side;
10760       change->actual_trigger_ce_value = CustomValue[x][y];
10761       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10762
10763       /* special case: trigger element not at (x,y) position for some events */
10764       if (check_trigger_element)
10765       {
10766         static struct
10767         {
10768           int dx, dy;
10769         } move_xy[] =
10770           {
10771             {  0,  0 },
10772             { -1,  0 },
10773             { +1,  0 },
10774             {  0,  0 },
10775             {  0, -1 },
10776             {  0,  0 }, { 0, 0 }, { 0, 0 },
10777             {  0, +1 }
10778           };
10779
10780         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10781         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10782
10783         change->actual_trigger_ce_value = CustomValue[xx][yy];
10784         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10785       }
10786
10787       if (change->can_change && !change_done)
10788       {
10789         ChangeDelay[x][y] = 1;
10790         ChangeEvent[x][y] = trigger_event;
10791
10792         HandleElementChange(x, y, p);
10793
10794         change_done = TRUE;
10795       }
10796       else if (change->has_action)
10797       {
10798         ExecuteCustomElementAction(x, y, element, p);
10799         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10800       }
10801     }
10802   }
10803
10804   RECURSION_LOOP_DETECTION_END();
10805
10806   return change_done;
10807 }
10808
10809 static void PlayPlayerSound(struct PlayerInfo *player)
10810 {
10811   int jx = player->jx, jy = player->jy;
10812   int sound_element = player->artwork_element;
10813   int last_action = player->last_action_waiting;
10814   int action = player->action_waiting;
10815
10816   if (player->is_waiting)
10817   {
10818     if (action != last_action)
10819       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10820     else
10821       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10822   }
10823   else
10824   {
10825     if (action != last_action)
10826       StopSound(element_info[sound_element].sound[last_action]);
10827
10828     if (last_action == ACTION_SLEEPING)
10829       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10830   }
10831 }
10832
10833 static void PlayAllPlayersSound()
10834 {
10835   int i;
10836
10837   for (i = 0; i < MAX_PLAYERS; i++)
10838     if (stored_player[i].active)
10839       PlayPlayerSound(&stored_player[i]);
10840 }
10841
10842 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10843 {
10844   boolean last_waiting = player->is_waiting;
10845   int move_dir = player->MovDir;
10846
10847   player->dir_waiting = move_dir;
10848   player->last_action_waiting = player->action_waiting;
10849
10850   if (is_waiting)
10851   {
10852     if (!last_waiting)          /* not waiting -> waiting */
10853     {
10854       player->is_waiting = TRUE;
10855
10856       player->frame_counter_bored =
10857         FrameCounter +
10858         game.player_boring_delay_fixed +
10859         GetSimpleRandom(game.player_boring_delay_random);
10860       player->frame_counter_sleeping =
10861         FrameCounter +
10862         game.player_sleeping_delay_fixed +
10863         GetSimpleRandom(game.player_sleeping_delay_random);
10864
10865       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10866     }
10867
10868     if (game.player_sleeping_delay_fixed +
10869         game.player_sleeping_delay_random > 0 &&
10870         player->anim_delay_counter == 0 &&
10871         player->post_delay_counter == 0 &&
10872         FrameCounter >= player->frame_counter_sleeping)
10873       player->is_sleeping = TRUE;
10874     else if (game.player_boring_delay_fixed +
10875              game.player_boring_delay_random > 0 &&
10876              FrameCounter >= player->frame_counter_bored)
10877       player->is_bored = TRUE;
10878
10879     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10880                               player->is_bored ? ACTION_BORING :
10881                               ACTION_WAITING);
10882
10883     if (player->is_sleeping && player->use_murphy)
10884     {
10885       /* special case for sleeping Murphy when leaning against non-free tile */
10886
10887       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10888           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10889            !IS_MOVING(player->jx - 1, player->jy)))
10890         move_dir = MV_LEFT;
10891       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10892                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10893                 !IS_MOVING(player->jx + 1, player->jy)))
10894         move_dir = MV_RIGHT;
10895       else
10896         player->is_sleeping = FALSE;
10897
10898       player->dir_waiting = move_dir;
10899     }
10900
10901     if (player->is_sleeping)
10902     {
10903       if (player->num_special_action_sleeping > 0)
10904       {
10905         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10906         {
10907           int last_special_action = player->special_action_sleeping;
10908           int num_special_action = player->num_special_action_sleeping;
10909           int special_action =
10910             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10911              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10912              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10913              last_special_action + 1 : ACTION_SLEEPING);
10914           int special_graphic =
10915             el_act_dir2img(player->artwork_element, special_action, move_dir);
10916
10917           player->anim_delay_counter =
10918             graphic_info[special_graphic].anim_delay_fixed +
10919             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10920           player->post_delay_counter =
10921             graphic_info[special_graphic].post_delay_fixed +
10922             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10923
10924           player->special_action_sleeping = special_action;
10925         }
10926
10927         if (player->anim_delay_counter > 0)
10928         {
10929           player->action_waiting = player->special_action_sleeping;
10930           player->anim_delay_counter--;
10931         }
10932         else if (player->post_delay_counter > 0)
10933         {
10934           player->post_delay_counter--;
10935         }
10936       }
10937     }
10938     else if (player->is_bored)
10939     {
10940       if (player->num_special_action_bored > 0)
10941       {
10942         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10943         {
10944           int special_action =
10945             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10946           int special_graphic =
10947             el_act_dir2img(player->artwork_element, special_action, move_dir);
10948
10949           player->anim_delay_counter =
10950             graphic_info[special_graphic].anim_delay_fixed +
10951             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10952           player->post_delay_counter =
10953             graphic_info[special_graphic].post_delay_fixed +
10954             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10955
10956           player->special_action_bored = special_action;
10957         }
10958
10959         if (player->anim_delay_counter > 0)
10960         {
10961           player->action_waiting = player->special_action_bored;
10962           player->anim_delay_counter--;
10963         }
10964         else if (player->post_delay_counter > 0)
10965         {
10966           player->post_delay_counter--;
10967         }
10968       }
10969     }
10970   }
10971   else if (last_waiting)        /* waiting -> not waiting */
10972   {
10973     player->is_waiting = FALSE;
10974     player->is_bored = FALSE;
10975     player->is_sleeping = FALSE;
10976
10977     player->frame_counter_bored = -1;
10978     player->frame_counter_sleeping = -1;
10979
10980     player->anim_delay_counter = 0;
10981     player->post_delay_counter = 0;
10982
10983     player->dir_waiting = player->MovDir;
10984     player->action_waiting = ACTION_DEFAULT;
10985
10986     player->special_action_bored = ACTION_DEFAULT;
10987     player->special_action_sleeping = ACTION_DEFAULT;
10988   }
10989 }
10990
10991 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10992 {
10993   if ((!player->is_moving  && player->was_moving) ||
10994       (player->MovPos == 0 && player->was_moving) ||
10995       (player->is_snapping && !player->was_snapping) ||
10996       (player->is_dropping && !player->was_dropping))
10997   {
10998     if (!CheckSaveEngineSnapshotToList())
10999       return;
11000
11001     player->was_moving = FALSE;
11002     player->was_snapping = TRUE;
11003     player->was_dropping = TRUE;
11004   }
11005   else
11006   {
11007     if (player->is_moving)
11008       player->was_moving = TRUE;
11009
11010     if (!player->is_snapping)
11011       player->was_snapping = FALSE;
11012
11013     if (!player->is_dropping)
11014       player->was_dropping = FALSE;
11015   }
11016 }
11017
11018 static void CheckSingleStepMode(struct PlayerInfo *player)
11019 {
11020   if (tape.single_step && tape.recording && !tape.pausing)
11021   {
11022     /* as it is called "single step mode", just return to pause mode when the
11023        player stopped moving after one tile (or never starts moving at all) */
11024     if (!player->is_moving &&
11025         !player->is_pushing &&
11026         !player->is_dropping_pressed)
11027     {
11028       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11029       SnapField(player, 0, 0);                  /* stop snapping */
11030     }
11031   }
11032
11033   CheckSaveEngineSnapshot(player);
11034 }
11035
11036 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11037 {
11038   int left      = player_action & JOY_LEFT;
11039   int right     = player_action & JOY_RIGHT;
11040   int up        = player_action & JOY_UP;
11041   int down      = player_action & JOY_DOWN;
11042   int button1   = player_action & JOY_BUTTON_1;
11043   int button2   = player_action & JOY_BUTTON_2;
11044   int dx        = (left ? -1 : right ? 1 : 0);
11045   int dy        = (up   ? -1 : down  ? 1 : 0);
11046
11047   if (!player->active || tape.pausing)
11048     return 0;
11049
11050   if (player_action)
11051   {
11052     if (button1)
11053       SnapField(player, dx, dy);
11054     else
11055     {
11056       if (button2)
11057         DropElement(player);
11058
11059       MovePlayer(player, dx, dy);
11060     }
11061
11062     CheckSingleStepMode(player);
11063
11064     SetPlayerWaiting(player, FALSE);
11065
11066     return player_action;
11067   }
11068   else
11069   {
11070     /* no actions for this player (no input at player's configured device) */
11071
11072     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11073     SnapField(player, 0, 0);
11074     CheckGravityMovementWhenNotMoving(player);
11075
11076     if (player->MovPos == 0)
11077       SetPlayerWaiting(player, TRUE);
11078
11079     if (player->MovPos == 0)    /* needed for tape.playing */
11080       player->is_moving = FALSE;
11081
11082     player->is_dropping = FALSE;
11083     player->is_dropping_pressed = FALSE;
11084     player->drop_pressed_delay = 0;
11085
11086     CheckSingleStepMode(player);
11087
11088     return 0;
11089   }
11090 }
11091
11092 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11093                                          byte *tape_action)
11094 {
11095   if (!tape.use_mouse)
11096     return;
11097
11098   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11099   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11100   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11101 }
11102
11103 static void SetTapeActionFromMouseAction(byte *tape_action,
11104                                          struct MouseActionInfo *mouse_action)
11105 {
11106   if (!tape.use_mouse)
11107     return;
11108
11109   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11110   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11111   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11112 }
11113
11114 static void CheckLevelTime()
11115 {
11116   int i;
11117
11118   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11119   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11120   {
11121     if (level.native_em_level->lev->home == 0)  /* all players at home */
11122     {
11123       PlayerWins(local_player);
11124
11125       AllPlayersGone = TRUE;
11126
11127       level.native_em_level->lev->home = -1;
11128     }
11129
11130     if (level.native_em_level->ply[0]->alive == 0 &&
11131         level.native_em_level->ply[1]->alive == 0 &&
11132         level.native_em_level->ply[2]->alive == 0 &&
11133         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11134       AllPlayersGone = TRUE;
11135   }
11136   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11137   {
11138     if (game_sp.LevelSolved &&
11139         !game_sp.GameOver)                              /* game won */
11140     {
11141       PlayerWins(local_player);
11142
11143       game_sp.GameOver = TRUE;
11144
11145       AllPlayersGone = TRUE;
11146     }
11147
11148     if (game_sp.GameOver)                               /* game lost */
11149       AllPlayersGone = TRUE;
11150   }
11151   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11152   {
11153     if (game_mm.level_solved &&
11154         !game_mm.game_over)                             /* game won */
11155     {
11156       PlayerWins(local_player);
11157
11158       game_mm.game_over = TRUE;
11159
11160       AllPlayersGone = TRUE;
11161     }
11162
11163     if (game_mm.game_over)                              /* game lost */
11164       AllPlayersGone = TRUE;
11165   }
11166
11167   if (TimeFrames >= FRAMES_PER_SECOND)
11168   {
11169     TimeFrames = 0;
11170     TapeTime++;
11171
11172     for (i = 0; i < MAX_PLAYERS; i++)
11173     {
11174       struct PlayerInfo *player = &stored_player[i];
11175
11176       if (SHIELD_ON(player))
11177       {
11178         player->shield_normal_time_left--;
11179
11180         if (player->shield_deadly_time_left > 0)
11181           player->shield_deadly_time_left--;
11182       }
11183     }
11184
11185     if (!local_player->LevelSolved && !level.use_step_counter)
11186     {
11187       TimePlayed++;
11188
11189       if (TimeLeft > 0)
11190       {
11191         TimeLeft--;
11192
11193         if (TimeLeft <= 10 && setup.time_limit)
11194           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11195
11196         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11197            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11198
11199         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11200
11201         if (!TimeLeft && setup.time_limit)
11202         {
11203           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11204             level.native_em_level->lev->killed_out_of_time = TRUE;
11205           else
11206             for (i = 0; i < MAX_PLAYERS; i++)
11207               KillPlayer(&stored_player[i]);
11208         }
11209       }
11210       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11211       {
11212         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11213       }
11214
11215       level.native_em_level->lev->time =
11216         (game.no_time_limit ? TimePlayed : TimeLeft);
11217     }
11218
11219     if (tape.recording || tape.playing)
11220       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11221   }
11222
11223   if (tape.recording || tape.playing)
11224     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11225
11226   UpdateAndDisplayGameControlValues();
11227 }
11228
11229 void AdvanceFrameAndPlayerCounters(int player_nr)
11230 {
11231   int i;
11232
11233   /* advance frame counters (global frame counter and time frame counter) */
11234   FrameCounter++;
11235   TimeFrames++;
11236
11237   /* advance player counters (counters for move delay, move animation etc.) */
11238   for (i = 0; i < MAX_PLAYERS; i++)
11239   {
11240     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11241     int move_delay_value = stored_player[i].move_delay_value;
11242     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11243
11244     if (!advance_player_counters)       /* not all players may be affected */
11245       continue;
11246
11247     if (move_frames == 0)       /* less than one move per game frame */
11248     {
11249       int stepsize = TILEX / move_delay_value;
11250       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11251       int count = (stored_player[i].is_moving ?
11252                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11253
11254       if (count % delay == 0)
11255         move_frames = 1;
11256     }
11257
11258     stored_player[i].Frame += move_frames;
11259
11260     if (stored_player[i].MovPos != 0)
11261       stored_player[i].StepFrame += move_frames;
11262
11263     if (stored_player[i].move_delay > 0)
11264       stored_player[i].move_delay--;
11265
11266     /* due to bugs in previous versions, counter must count up, not down */
11267     if (stored_player[i].push_delay != -1)
11268       stored_player[i].push_delay++;
11269
11270     if (stored_player[i].drop_delay > 0)
11271       stored_player[i].drop_delay--;
11272
11273     if (stored_player[i].is_dropping_pressed)
11274       stored_player[i].drop_pressed_delay++;
11275   }
11276 }
11277
11278 void StartGameActions(boolean init_network_game, boolean record_tape,
11279                       int random_seed)
11280 {
11281   unsigned int new_random_seed = InitRND(random_seed);
11282
11283   if (record_tape)
11284     TapeStartRecording(new_random_seed);
11285
11286   if (init_network_game)
11287   {
11288     SendToServer_StartPlaying();
11289
11290     return;
11291   }
11292
11293   InitGame();
11294 }
11295
11296 void GameActionsExt()
11297 {
11298 #if 0
11299   static unsigned int game_frame_delay = 0;
11300 #endif
11301   unsigned int game_frame_delay_value;
11302   byte *recorded_player_action;
11303   byte summarized_player_action = 0;
11304   byte tape_action[MAX_PLAYERS];
11305   int i;
11306
11307   /* detect endless loops, caused by custom element programming */
11308   if (recursion_loop_detected && recursion_loop_depth == 0)
11309   {
11310     char *message = getStringCat3("Internal Error! Element ",
11311                                   EL_NAME(recursion_loop_element),
11312                                   " caused endless loop! Quit the game?");
11313
11314     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11315           EL_NAME(recursion_loop_element));
11316
11317     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11318
11319     recursion_loop_detected = FALSE;    /* if game should be continued */
11320
11321     free(message);
11322
11323     return;
11324   }
11325
11326   if (game.restart_level)
11327     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11328
11329   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11330   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11331   {
11332     if (level.native_em_level->lev->home == 0)  /* all players at home */
11333     {
11334       PlayerWins(local_player);
11335
11336       AllPlayersGone = TRUE;
11337
11338       level.native_em_level->lev->home = -1;
11339     }
11340
11341     if (level.native_em_level->ply[0]->alive == 0 &&
11342         level.native_em_level->ply[1]->alive == 0 &&
11343         level.native_em_level->ply[2]->alive == 0 &&
11344         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11345       AllPlayersGone = TRUE;
11346   }
11347   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11348   {
11349     if (game_sp.LevelSolved &&
11350         !game_sp.GameOver)                              /* game won */
11351     {
11352       PlayerWins(local_player);
11353
11354       game_sp.GameOver = TRUE;
11355
11356       AllPlayersGone = TRUE;
11357     }
11358
11359     if (game_sp.GameOver)                               /* game lost */
11360       AllPlayersGone = TRUE;
11361   }
11362   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11363   {
11364     if (game_mm.level_solved &&
11365         !game_mm.game_over)                             /* game won */
11366     {
11367       PlayerWins(local_player);
11368
11369       game_mm.game_over = TRUE;
11370
11371       AllPlayersGone = TRUE;
11372     }
11373
11374     if (game_mm.game_over)                              /* game lost */
11375       AllPlayersGone = TRUE;
11376   }
11377
11378   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11379     GameWon();
11380
11381   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11382     TapeStop();
11383
11384   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11385     return;
11386
11387   game_frame_delay_value =
11388     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11389
11390   if (tape.playing && tape.warp_forward && !tape.pausing)
11391     game_frame_delay_value = 0;
11392
11393   SetVideoFrameDelay(game_frame_delay_value);
11394
11395 #if 0
11396 #if 0
11397   /* ---------- main game synchronization point ---------- */
11398
11399   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11400
11401   printf("::: skip == %d\n", skip);
11402
11403 #else
11404   /* ---------- main game synchronization point ---------- */
11405
11406   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11407 #endif
11408 #endif
11409
11410   if (network_playing && !network_player_action_received)
11411   {
11412     /* try to get network player actions in time */
11413
11414     /* last chance to get network player actions without main loop delay */
11415     HandleNetworking();
11416
11417     /* game was quit by network peer */
11418     if (game_status != GAME_MODE_PLAYING)
11419       return;
11420
11421     if (!network_player_action_received)
11422       return;           /* failed to get network player actions in time */
11423
11424     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11425   }
11426
11427   if (tape.pausing)
11428     return;
11429
11430   /* at this point we know that we really continue executing the game */
11431
11432   network_player_action_received = FALSE;
11433
11434   /* when playing tape, read previously recorded player input from tape data */
11435   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11436
11437   local_player->effective_mouse_action = local_player->mouse_action;
11438
11439   if (recorded_player_action != NULL)
11440     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11441                                  recorded_player_action);
11442
11443   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11444   if (tape.pausing)
11445     return;
11446
11447   if (tape.set_centered_player)
11448   {
11449     game.centered_player_nr_next = tape.centered_player_nr_next;
11450     game.set_centered_player = TRUE;
11451   }
11452
11453   for (i = 0; i < MAX_PLAYERS; i++)
11454   {
11455     summarized_player_action |= stored_player[i].action;
11456
11457     if (!network_playing && (game.team_mode || tape.playing))
11458       stored_player[i].effective_action = stored_player[i].action;
11459   }
11460
11461   if (network_playing)
11462     SendToServer_MovePlayer(summarized_player_action);
11463
11464   // summarize all actions at local players mapped input device position
11465   // (this allows using different input devices in single player mode)
11466   if (!network.enabled && !game.team_mode)
11467     stored_player[map_player_action[local_player->index_nr]].effective_action =
11468       summarized_player_action;
11469
11470   if (tape.recording &&
11471       setup.team_mode &&
11472       setup.input_on_focus &&
11473       game.centered_player_nr != -1)
11474   {
11475     for (i = 0; i < MAX_PLAYERS; i++)
11476       stored_player[i].effective_action =
11477         (i == game.centered_player_nr ? summarized_player_action : 0);
11478   }
11479
11480   if (recorded_player_action != NULL)
11481     for (i = 0; i < MAX_PLAYERS; i++)
11482       stored_player[i].effective_action = recorded_player_action[i];
11483
11484   for (i = 0; i < MAX_PLAYERS; i++)
11485   {
11486     tape_action[i] = stored_player[i].effective_action;
11487
11488     /* (this may happen in the RND game engine if a player was not present on
11489        the playfield on level start, but appeared later from a custom element */
11490     if (setup.team_mode &&
11491         tape.recording &&
11492         tape_action[i] &&
11493         !tape.player_participates[i])
11494       tape.player_participates[i] = TRUE;
11495   }
11496
11497   SetTapeActionFromMouseAction(tape_action,
11498                                &local_player->effective_mouse_action);
11499
11500   /* only record actions from input devices, but not programmed actions */
11501   if (tape.recording)
11502     TapeRecordAction(tape_action);
11503
11504 #if USE_NEW_PLAYER_ASSIGNMENTS
11505   // !!! also map player actions in single player mode !!!
11506   // if (game.team_mode)
11507   if (1)
11508   {
11509     byte mapped_action[MAX_PLAYERS];
11510
11511 #if DEBUG_PLAYER_ACTIONS
11512     printf(":::");
11513     for (i = 0; i < MAX_PLAYERS; i++)
11514       printf(" %d, ", stored_player[i].effective_action);
11515 #endif
11516
11517     for (i = 0; i < MAX_PLAYERS; i++)
11518       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11519
11520     for (i = 0; i < MAX_PLAYERS; i++)
11521       stored_player[i].effective_action = mapped_action[i];
11522
11523 #if DEBUG_PLAYER_ACTIONS
11524     printf(" =>");
11525     for (i = 0; i < MAX_PLAYERS; i++)
11526       printf(" %d, ", stored_player[i].effective_action);
11527     printf("\n");
11528 #endif
11529   }
11530 #if DEBUG_PLAYER_ACTIONS
11531   else
11532   {
11533     printf(":::");
11534     for (i = 0; i < MAX_PLAYERS; i++)
11535       printf(" %d, ", stored_player[i].effective_action);
11536     printf("\n");
11537   }
11538 #endif
11539 #endif
11540
11541   for (i = 0; i < MAX_PLAYERS; i++)
11542   {
11543     // allow engine snapshot in case of changed movement attempt
11544     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11545         (stored_player[i].effective_action & KEY_MOTION))
11546       game.snapshot.changed_action = TRUE;
11547
11548     // allow engine snapshot in case of snapping/dropping attempt
11549     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11550         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11551       game.snapshot.changed_action = TRUE;
11552
11553     game.snapshot.last_action[i] = stored_player[i].effective_action;
11554   }
11555
11556   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11557   {
11558     GameActions_EM_Main();
11559   }
11560   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11561   {
11562     GameActions_SP_Main();
11563   }
11564   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11565   {
11566     GameActions_MM_Main();
11567   }
11568   else
11569   {
11570     GameActions_RND_Main();
11571   }
11572
11573   BlitScreenToBitmap(backbuffer);
11574
11575   CheckLevelTime();
11576
11577   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11578
11579   if (global.show_frames_per_second)
11580   {
11581     static unsigned int fps_counter = 0;
11582     static int fps_frames = 0;
11583     unsigned int fps_delay_ms = Counter() - fps_counter;
11584
11585     fps_frames++;
11586
11587     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11588     {
11589       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11590
11591       fps_frames = 0;
11592       fps_counter = Counter();
11593
11594       /* always draw FPS to screen after FPS value was updated */
11595       redraw_mask |= REDRAW_FPS;
11596     }
11597
11598     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11599     if (GetDrawDeactivationMask() == REDRAW_NONE)
11600       redraw_mask |= REDRAW_FPS;
11601   }
11602 }
11603
11604 static void GameActions_CheckSaveEngineSnapshot()
11605 {
11606   if (!game.snapshot.save_snapshot)
11607     return;
11608
11609   // clear flag for saving snapshot _before_ saving snapshot
11610   game.snapshot.save_snapshot = FALSE;
11611
11612   SaveEngineSnapshotToList();
11613 }
11614
11615 void GameActions()
11616 {
11617   GameActionsExt();
11618
11619   GameActions_CheckSaveEngineSnapshot();
11620 }
11621
11622 void GameActions_EM_Main()
11623 {
11624   byte effective_action[MAX_PLAYERS];
11625   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11626   int i;
11627
11628   for (i = 0; i < MAX_PLAYERS; i++)
11629     effective_action[i] = stored_player[i].effective_action;
11630
11631   GameActions_EM(effective_action, warp_mode);
11632 }
11633
11634 void GameActions_SP_Main()
11635 {
11636   byte effective_action[MAX_PLAYERS];
11637   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11638   int i;
11639
11640   for (i = 0; i < MAX_PLAYERS; i++)
11641     effective_action[i] = stored_player[i].effective_action;
11642
11643   GameActions_SP(effective_action, warp_mode);
11644
11645   for (i = 0; i < MAX_PLAYERS; i++)
11646   {
11647     if (stored_player[i].force_dropping)
11648       stored_player[i].action |= KEY_BUTTON_DROP;
11649
11650     stored_player[i].force_dropping = FALSE;
11651   }
11652 }
11653
11654 void GameActions_MM_Main()
11655 {
11656   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11657
11658   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11659 }
11660
11661 void GameActions_RND_Main()
11662 {
11663   GameActions_RND();
11664 }
11665
11666 void GameActions_RND()
11667 {
11668   int magic_wall_x = 0, magic_wall_y = 0;
11669   int i, x, y, element, graphic, last_gfx_frame;
11670
11671   InitPlayfieldScanModeVars();
11672
11673   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11674   {
11675     SCAN_PLAYFIELD(x, y)
11676     {
11677       ChangeCount[x][y] = 0;
11678       ChangeEvent[x][y] = -1;
11679     }
11680   }
11681
11682   if (game.set_centered_player)
11683   {
11684     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11685
11686     /* switching to "all players" only possible if all players fit to screen */
11687     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11688     {
11689       game.centered_player_nr_next = game.centered_player_nr;
11690       game.set_centered_player = FALSE;
11691     }
11692
11693     /* do not switch focus to non-existing (or non-active) player */
11694     if (game.centered_player_nr_next >= 0 &&
11695         !stored_player[game.centered_player_nr_next].active)
11696     {
11697       game.centered_player_nr_next = game.centered_player_nr;
11698       game.set_centered_player = FALSE;
11699     }
11700   }
11701
11702   if (game.set_centered_player &&
11703       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11704   {
11705     int sx, sy;
11706
11707     if (game.centered_player_nr_next == -1)
11708     {
11709       setScreenCenteredToAllPlayers(&sx, &sy);
11710     }
11711     else
11712     {
11713       sx = stored_player[game.centered_player_nr_next].jx;
11714       sy = stored_player[game.centered_player_nr_next].jy;
11715     }
11716
11717     game.centered_player_nr = game.centered_player_nr_next;
11718     game.set_centered_player = FALSE;
11719
11720     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11721     DrawGameDoorValues();
11722   }
11723
11724   for (i = 0; i < MAX_PLAYERS; i++)
11725   {
11726     int actual_player_action = stored_player[i].effective_action;
11727
11728 #if 1
11729     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11730        - rnd_equinox_tetrachloride 048
11731        - rnd_equinox_tetrachloride_ii 096
11732        - rnd_emanuel_schmieg 002
11733        - doctor_sloan_ww 001, 020
11734     */
11735     if (stored_player[i].MovPos == 0)
11736       CheckGravityMovement(&stored_player[i]);
11737 #endif
11738
11739     /* overwrite programmed action with tape action */
11740     if (stored_player[i].programmed_action)
11741       actual_player_action = stored_player[i].programmed_action;
11742
11743     PlayerActions(&stored_player[i], actual_player_action);
11744
11745     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11746   }
11747
11748   ScrollScreen(NULL, SCROLL_GO_ON);
11749
11750   /* for backwards compatibility, the following code emulates a fixed bug that
11751      occured when pushing elements (causing elements that just made their last
11752      pushing step to already (if possible) make their first falling step in the
11753      same game frame, which is bad); this code is also needed to use the famous
11754      "spring push bug" which is used in older levels and might be wanted to be
11755      used also in newer levels, but in this case the buggy pushing code is only
11756      affecting the "spring" element and no other elements */
11757
11758   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11759   {
11760     for (i = 0; i < MAX_PLAYERS; i++)
11761     {
11762       struct PlayerInfo *player = &stored_player[i];
11763       int x = player->jx;
11764       int y = player->jy;
11765
11766       if (player->active && player->is_pushing && player->is_moving &&
11767           IS_MOVING(x, y) &&
11768           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11769            Feld[x][y] == EL_SPRING))
11770       {
11771         ContinueMoving(x, y);
11772
11773         /* continue moving after pushing (this is actually a bug) */
11774         if (!IS_MOVING(x, y))
11775           Stop[x][y] = FALSE;
11776       }
11777     }
11778   }
11779
11780   SCAN_PLAYFIELD(x, y)
11781   {
11782     ChangeCount[x][y] = 0;
11783     ChangeEvent[x][y] = -1;
11784
11785     /* this must be handled before main playfield loop */
11786     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11787     {
11788       MovDelay[x][y]--;
11789       if (MovDelay[x][y] <= 0)
11790         RemoveField(x, y);
11791     }
11792
11793     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11794     {
11795       MovDelay[x][y]--;
11796       if (MovDelay[x][y] <= 0)
11797       {
11798         RemoveField(x, y);
11799         TEST_DrawLevelField(x, y);
11800
11801         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11802       }
11803     }
11804
11805 #if DEBUG
11806     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11807     {
11808       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11809       printf("GameActions(): This should never happen!\n");
11810
11811       ChangePage[x][y] = -1;
11812     }
11813 #endif
11814
11815     Stop[x][y] = FALSE;
11816     if (WasJustMoving[x][y] > 0)
11817       WasJustMoving[x][y]--;
11818     if (WasJustFalling[x][y] > 0)
11819       WasJustFalling[x][y]--;
11820     if (CheckCollision[x][y] > 0)
11821       CheckCollision[x][y]--;
11822     if (CheckImpact[x][y] > 0)
11823       CheckImpact[x][y]--;
11824
11825     GfxFrame[x][y]++;
11826
11827     /* reset finished pushing action (not done in ContinueMoving() to allow
11828        continuous pushing animation for elements with zero push delay) */
11829     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11830     {
11831       ResetGfxAnimation(x, y);
11832       TEST_DrawLevelField(x, y);
11833     }
11834
11835 #if DEBUG
11836     if (IS_BLOCKED(x, y))
11837     {
11838       int oldx, oldy;
11839
11840       Blocked2Moving(x, y, &oldx, &oldy);
11841       if (!IS_MOVING(oldx, oldy))
11842       {
11843         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11844         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11845         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11846         printf("GameActions(): This should never happen!\n");
11847       }
11848     }
11849 #endif
11850   }
11851
11852   SCAN_PLAYFIELD(x, y)
11853   {
11854     element = Feld[x][y];
11855     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11856     last_gfx_frame = GfxFrame[x][y];
11857
11858     ResetGfxFrame(x, y);
11859
11860     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11861       DrawLevelGraphicAnimation(x, y, graphic);
11862
11863     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11864         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11865       ResetRandomAnimationValue(x, y);
11866
11867     SetRandomAnimationValue(x, y);
11868
11869     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11870
11871     if (IS_INACTIVE(element))
11872     {
11873       if (IS_ANIMATED(graphic))
11874         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11875
11876       continue;
11877     }
11878
11879     /* this may take place after moving, so 'element' may have changed */
11880     if (IS_CHANGING(x, y) &&
11881         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11882     {
11883       int page = element_info[element].event_page_nr[CE_DELAY];
11884
11885       HandleElementChange(x, y, page);
11886
11887       element = Feld[x][y];
11888       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11889     }
11890
11891     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11892     {
11893       StartMoving(x, y);
11894
11895       element = Feld[x][y];
11896       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11897
11898       if (IS_ANIMATED(graphic) &&
11899           !IS_MOVING(x, y) &&
11900           !Stop[x][y])
11901         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11902
11903       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11904         TEST_DrawTwinkleOnField(x, y);
11905     }
11906     else if (element == EL_ACID)
11907     {
11908       if (!Stop[x][y])
11909         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11910     }
11911     else if ((element == EL_EXIT_OPEN ||
11912               element == EL_EM_EXIT_OPEN ||
11913               element == EL_SP_EXIT_OPEN ||
11914               element == EL_STEEL_EXIT_OPEN ||
11915               element == EL_EM_STEEL_EXIT_OPEN ||
11916               element == EL_SP_TERMINAL ||
11917               element == EL_SP_TERMINAL_ACTIVE ||
11918               element == EL_EXTRA_TIME ||
11919               element == EL_SHIELD_NORMAL ||
11920               element == EL_SHIELD_DEADLY) &&
11921              IS_ANIMATED(graphic))
11922       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11923     else if (IS_MOVING(x, y))
11924       ContinueMoving(x, y);
11925     else if (IS_ACTIVE_BOMB(element))
11926       CheckDynamite(x, y);
11927     else if (element == EL_AMOEBA_GROWING)
11928       AmoebeWaechst(x, y);
11929     else if (element == EL_AMOEBA_SHRINKING)
11930       AmoebaDisappearing(x, y);
11931
11932 #if !USE_NEW_AMOEBA_CODE
11933     else if (IS_AMOEBALIVE(element))
11934       AmoebeAbleger(x, y);
11935 #endif
11936
11937     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11938       Life(x, y);
11939     else if (element == EL_EXIT_CLOSED)
11940       CheckExit(x, y);
11941     else if (element == EL_EM_EXIT_CLOSED)
11942       CheckExitEM(x, y);
11943     else if (element == EL_STEEL_EXIT_CLOSED)
11944       CheckExitSteel(x, y);
11945     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11946       CheckExitSteelEM(x, y);
11947     else if (element == EL_SP_EXIT_CLOSED)
11948       CheckExitSP(x, y);
11949     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11950              element == EL_EXPANDABLE_STEELWALL_GROWING)
11951       MauerWaechst(x, y);
11952     else if (element == EL_EXPANDABLE_WALL ||
11953              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11954              element == EL_EXPANDABLE_WALL_VERTICAL ||
11955              element == EL_EXPANDABLE_WALL_ANY ||
11956              element == EL_BD_EXPANDABLE_WALL)
11957       MauerAbleger(x, y);
11958     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11959              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11960              element == EL_EXPANDABLE_STEELWALL_ANY)
11961       MauerAblegerStahl(x, y);
11962     else if (element == EL_FLAMES)
11963       CheckForDragon(x, y);
11964     else if (element == EL_EXPLOSION)
11965       ; /* drawing of correct explosion animation is handled separately */
11966     else if (element == EL_ELEMENT_SNAPPING ||
11967              element == EL_DIAGONAL_SHRINKING ||
11968              element == EL_DIAGONAL_GROWING)
11969     {
11970       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11971
11972       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11973     }
11974     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11975       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11976
11977     if (IS_BELT_ACTIVE(element))
11978       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11979
11980     if (game.magic_wall_active)
11981     {
11982       int jx = local_player->jx, jy = local_player->jy;
11983
11984       /* play the element sound at the position nearest to the player */
11985       if ((element == EL_MAGIC_WALL_FULL ||
11986            element == EL_MAGIC_WALL_ACTIVE ||
11987            element == EL_MAGIC_WALL_EMPTYING ||
11988            element == EL_BD_MAGIC_WALL_FULL ||
11989            element == EL_BD_MAGIC_WALL_ACTIVE ||
11990            element == EL_BD_MAGIC_WALL_EMPTYING ||
11991            element == EL_DC_MAGIC_WALL_FULL ||
11992            element == EL_DC_MAGIC_WALL_ACTIVE ||
11993            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11994           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11995       {
11996         magic_wall_x = x;
11997         magic_wall_y = y;
11998       }
11999     }
12000   }
12001
12002 #if USE_NEW_AMOEBA_CODE
12003   /* new experimental amoeba growth stuff */
12004   if (!(FrameCounter % 8))
12005   {
12006     static unsigned int random = 1684108901;
12007
12008     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12009     {
12010       x = RND(lev_fieldx);
12011       y = RND(lev_fieldy);
12012       element = Feld[x][y];
12013
12014       if (!IS_PLAYER(x,y) &&
12015           (element == EL_EMPTY ||
12016            CAN_GROW_INTO(element) ||
12017            element == EL_QUICKSAND_EMPTY ||
12018            element == EL_QUICKSAND_FAST_EMPTY ||
12019            element == EL_ACID_SPLASH_LEFT ||
12020            element == EL_ACID_SPLASH_RIGHT))
12021       {
12022         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12023             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12024             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12025             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12026           Feld[x][y] = EL_AMOEBA_DROP;
12027       }
12028
12029       random = random * 129 + 1;
12030     }
12031   }
12032 #endif
12033
12034   game.explosions_delayed = FALSE;
12035
12036   SCAN_PLAYFIELD(x, y)
12037   {
12038     element = Feld[x][y];
12039
12040     if (ExplodeField[x][y])
12041       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12042     else if (element == EL_EXPLOSION)
12043       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12044
12045     ExplodeField[x][y] = EX_TYPE_NONE;
12046   }
12047
12048   game.explosions_delayed = TRUE;
12049
12050   if (game.magic_wall_active)
12051   {
12052     if (!(game.magic_wall_time_left % 4))
12053     {
12054       int element = Feld[magic_wall_x][magic_wall_y];
12055
12056       if (element == EL_BD_MAGIC_WALL_FULL ||
12057           element == EL_BD_MAGIC_WALL_ACTIVE ||
12058           element == EL_BD_MAGIC_WALL_EMPTYING)
12059         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12060       else if (element == EL_DC_MAGIC_WALL_FULL ||
12061                element == EL_DC_MAGIC_WALL_ACTIVE ||
12062                element == EL_DC_MAGIC_WALL_EMPTYING)
12063         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12064       else
12065         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12066     }
12067
12068     if (game.magic_wall_time_left > 0)
12069     {
12070       game.magic_wall_time_left--;
12071
12072       if (!game.magic_wall_time_left)
12073       {
12074         SCAN_PLAYFIELD(x, y)
12075         {
12076           element = Feld[x][y];
12077
12078           if (element == EL_MAGIC_WALL_ACTIVE ||
12079               element == EL_MAGIC_WALL_FULL)
12080           {
12081             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12082             TEST_DrawLevelField(x, y);
12083           }
12084           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12085                    element == EL_BD_MAGIC_WALL_FULL)
12086           {
12087             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12088             TEST_DrawLevelField(x, y);
12089           }
12090           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12091                    element == EL_DC_MAGIC_WALL_FULL)
12092           {
12093             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12094             TEST_DrawLevelField(x, y);
12095           }
12096         }
12097
12098         game.magic_wall_active = FALSE;
12099       }
12100     }
12101   }
12102
12103   if (game.light_time_left > 0)
12104   {
12105     game.light_time_left--;
12106
12107     if (game.light_time_left == 0)
12108       RedrawAllLightSwitchesAndInvisibleElements();
12109   }
12110
12111   if (game.timegate_time_left > 0)
12112   {
12113     game.timegate_time_left--;
12114
12115     if (game.timegate_time_left == 0)
12116       CloseAllOpenTimegates();
12117   }
12118
12119   if (game.lenses_time_left > 0)
12120   {
12121     game.lenses_time_left--;
12122
12123     if (game.lenses_time_left == 0)
12124       RedrawAllInvisibleElementsForLenses();
12125   }
12126
12127   if (game.magnify_time_left > 0)
12128   {
12129     game.magnify_time_left--;
12130
12131     if (game.magnify_time_left == 0)
12132       RedrawAllInvisibleElementsForMagnifier();
12133   }
12134
12135   for (i = 0; i < MAX_PLAYERS; i++)
12136   {
12137     struct PlayerInfo *player = &stored_player[i];
12138
12139     if (SHIELD_ON(player))
12140     {
12141       if (player->shield_deadly_time_left)
12142         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12143       else if (player->shield_normal_time_left)
12144         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12145     }
12146   }
12147
12148 #if USE_DELAYED_GFX_REDRAW
12149   SCAN_PLAYFIELD(x, y)
12150   {
12151     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12152     {
12153       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12154          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12155
12156       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12157         DrawLevelField(x, y);
12158
12159       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12160         DrawLevelFieldCrumbled(x, y);
12161
12162       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12163         DrawLevelFieldCrumbledNeighbours(x, y);
12164
12165       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12166         DrawTwinkleOnField(x, y);
12167     }
12168
12169     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12170   }
12171 #endif
12172
12173   DrawAllPlayers();
12174   PlayAllPlayersSound();
12175
12176   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12177   {
12178     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12179
12180     local_player->show_envelope = 0;
12181   }
12182
12183   /* use random number generator in every frame to make it less predictable */
12184   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12185     RND(1);
12186 }
12187
12188 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12189 {
12190   int min_x = x, min_y = y, max_x = x, max_y = y;
12191   int i;
12192
12193   for (i = 0; i < MAX_PLAYERS; i++)
12194   {
12195     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12196
12197     if (!stored_player[i].active || &stored_player[i] == player)
12198       continue;
12199
12200     min_x = MIN(min_x, jx);
12201     min_y = MIN(min_y, jy);
12202     max_x = MAX(max_x, jx);
12203     max_y = MAX(max_y, jy);
12204   }
12205
12206   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12207 }
12208
12209 static boolean AllPlayersInVisibleScreen()
12210 {
12211   int i;
12212
12213   for (i = 0; i < MAX_PLAYERS; i++)
12214   {
12215     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12216
12217     if (!stored_player[i].active)
12218       continue;
12219
12220     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12221       return FALSE;
12222   }
12223
12224   return TRUE;
12225 }
12226
12227 void ScrollLevel(int dx, int dy)
12228 {
12229   int scroll_offset = 2 * TILEX_VAR;
12230   int x, y;
12231
12232   BlitBitmap(drawto_field, drawto_field,
12233              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12234              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12235              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12236              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12237              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12238              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12239
12240   if (dx != 0)
12241   {
12242     x = (dx == 1 ? BX1 : BX2);
12243     for (y = BY1; y <= BY2; y++)
12244       DrawScreenField(x, y);
12245   }
12246
12247   if (dy != 0)
12248   {
12249     y = (dy == 1 ? BY1 : BY2);
12250     for (x = BX1; x <= BX2; x++)
12251       DrawScreenField(x, y);
12252   }
12253
12254   redraw_mask |= REDRAW_FIELD;
12255 }
12256
12257 static boolean canFallDown(struct PlayerInfo *player)
12258 {
12259   int jx = player->jx, jy = player->jy;
12260
12261   return (IN_LEV_FIELD(jx, jy + 1) &&
12262           (IS_FREE(jx, jy + 1) ||
12263            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12264           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12265           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12266 }
12267
12268 static boolean canPassField(int x, int y, int move_dir)
12269 {
12270   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12271   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12272   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12273   int nextx = x + dx;
12274   int nexty = y + dy;
12275   int element = Feld[x][y];
12276
12277   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12278           !CAN_MOVE(element) &&
12279           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12280           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12281           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12282 }
12283
12284 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12285 {
12286   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12287   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12288   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12289   int newx = x + dx;
12290   int newy = y + dy;
12291
12292   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12293           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12294           (IS_DIGGABLE(Feld[newx][newy]) ||
12295            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12296            canPassField(newx, newy, move_dir)));
12297 }
12298
12299 static void CheckGravityMovement(struct PlayerInfo *player)
12300 {
12301   if (player->gravity && !player->programmed_action)
12302   {
12303     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12304     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12305     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12306     int jx = player->jx, jy = player->jy;
12307     boolean player_is_moving_to_valid_field =
12308       (!player_is_snapping &&
12309        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12310         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12311     boolean player_can_fall_down = canFallDown(player);
12312
12313     if (player_can_fall_down &&
12314         !player_is_moving_to_valid_field)
12315       player->programmed_action = MV_DOWN;
12316   }
12317 }
12318
12319 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12320 {
12321   return CheckGravityMovement(player);
12322
12323   if (player->gravity && !player->programmed_action)
12324   {
12325     int jx = player->jx, jy = player->jy;
12326     boolean field_under_player_is_free =
12327       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12328     boolean player_is_standing_on_valid_field =
12329       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12330        (IS_WALKABLE(Feld[jx][jy]) &&
12331         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12332
12333     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12334       player->programmed_action = MV_DOWN;
12335   }
12336 }
12337
12338 /*
12339   MovePlayerOneStep()
12340   -----------------------------------------------------------------------------
12341   dx, dy:               direction (non-diagonal) to try to move the player to
12342   real_dx, real_dy:     direction as read from input device (can be diagonal)
12343 */
12344
12345 boolean MovePlayerOneStep(struct PlayerInfo *player,
12346                           int dx, int dy, int real_dx, int real_dy)
12347 {
12348   int jx = player->jx, jy = player->jy;
12349   int new_jx = jx + dx, new_jy = jy + dy;
12350   int can_move;
12351   boolean player_can_move = !player->cannot_move;
12352
12353   if (!player->active || (!dx && !dy))
12354     return MP_NO_ACTION;
12355
12356   player->MovDir = (dx < 0 ? MV_LEFT :
12357                     dx > 0 ? MV_RIGHT :
12358                     dy < 0 ? MV_UP :
12359                     dy > 0 ? MV_DOWN :  MV_NONE);
12360
12361   if (!IN_LEV_FIELD(new_jx, new_jy))
12362     return MP_NO_ACTION;
12363
12364   if (!player_can_move)
12365   {
12366     if (player->MovPos == 0)
12367     {
12368       player->is_moving = FALSE;
12369       player->is_digging = FALSE;
12370       player->is_collecting = FALSE;
12371       player->is_snapping = FALSE;
12372       player->is_pushing = FALSE;
12373     }
12374   }
12375
12376   if (!network.enabled && game.centered_player_nr == -1 &&
12377       !AllPlayersInSight(player, new_jx, new_jy))
12378     return MP_NO_ACTION;
12379
12380   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12381   if (can_move != MP_MOVING)
12382     return can_move;
12383
12384   /* check if DigField() has caused relocation of the player */
12385   if (player->jx != jx || player->jy != jy)
12386     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12387
12388   StorePlayer[jx][jy] = 0;
12389   player->last_jx = jx;
12390   player->last_jy = jy;
12391   player->jx = new_jx;
12392   player->jy = new_jy;
12393   StorePlayer[new_jx][new_jy] = player->element_nr;
12394
12395   if (player->move_delay_value_next != -1)
12396   {
12397     player->move_delay_value = player->move_delay_value_next;
12398     player->move_delay_value_next = -1;
12399   }
12400
12401   player->MovPos =
12402     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12403
12404   player->step_counter++;
12405
12406   PlayerVisit[jx][jy] = FrameCounter;
12407
12408   player->is_moving = TRUE;
12409
12410 #if 1
12411   /* should better be called in MovePlayer(), but this breaks some tapes */
12412   ScrollPlayer(player, SCROLL_INIT);
12413 #endif
12414
12415   return MP_MOVING;
12416 }
12417
12418 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12419 {
12420   int jx = player->jx, jy = player->jy;
12421   int old_jx = jx, old_jy = jy;
12422   int moved = MP_NO_ACTION;
12423
12424   if (!player->active)
12425     return FALSE;
12426
12427   if (!dx && !dy)
12428   {
12429     if (player->MovPos == 0)
12430     {
12431       player->is_moving = FALSE;
12432       player->is_digging = FALSE;
12433       player->is_collecting = FALSE;
12434       player->is_snapping = FALSE;
12435       player->is_pushing = FALSE;
12436     }
12437
12438     return FALSE;
12439   }
12440
12441   if (player->move_delay > 0)
12442     return FALSE;
12443
12444   player->move_delay = -1;              /* set to "uninitialized" value */
12445
12446   /* store if player is automatically moved to next field */
12447   player->is_auto_moving = (player->programmed_action != MV_NONE);
12448
12449   /* remove the last programmed player action */
12450   player->programmed_action = 0;
12451
12452   if (player->MovPos)
12453   {
12454     /* should only happen if pre-1.2 tape recordings are played */
12455     /* this is only for backward compatibility */
12456
12457     int original_move_delay_value = player->move_delay_value;
12458
12459 #if DEBUG
12460     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12461            tape.counter);
12462 #endif
12463
12464     /* scroll remaining steps with finest movement resolution */
12465     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12466
12467     while (player->MovPos)
12468     {
12469       ScrollPlayer(player, SCROLL_GO_ON);
12470       ScrollScreen(NULL, SCROLL_GO_ON);
12471
12472       AdvanceFrameAndPlayerCounters(player->index_nr);
12473
12474       DrawAllPlayers();
12475       BackToFront_WithFrameDelay(0);
12476     }
12477
12478     player->move_delay_value = original_move_delay_value;
12479   }
12480
12481   player->is_active = FALSE;
12482
12483   if (player->last_move_dir & MV_HORIZONTAL)
12484   {
12485     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12486       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12487   }
12488   else
12489   {
12490     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12491       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12492   }
12493
12494   if (!moved && !player->is_active)
12495   {
12496     player->is_moving = FALSE;
12497     player->is_digging = FALSE;
12498     player->is_collecting = FALSE;
12499     player->is_snapping = FALSE;
12500     player->is_pushing = FALSE;
12501   }
12502
12503   jx = player->jx;
12504   jy = player->jy;
12505
12506   if (moved & MP_MOVING && !ScreenMovPos &&
12507       (player->index_nr == game.centered_player_nr ||
12508        game.centered_player_nr == -1))
12509   {
12510     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12511     int offset = game.scroll_delay_value;
12512
12513     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12514     {
12515       /* actual player has left the screen -- scroll in that direction */
12516       if (jx != old_jx)         /* player has moved horizontally */
12517         scroll_x += (jx - old_jx);
12518       else                      /* player has moved vertically */
12519         scroll_y += (jy - old_jy);
12520     }
12521     else
12522     {
12523       if (jx != old_jx)         /* player has moved horizontally */
12524       {
12525         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12526             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12527           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12528
12529         /* don't scroll over playfield boundaries */
12530         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12531           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12532
12533         /* don't scroll more than one field at a time */
12534         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12535
12536         /* don't scroll against the player's moving direction */
12537         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12538             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12539           scroll_x = old_scroll_x;
12540       }
12541       else                      /* player has moved vertically */
12542       {
12543         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12544             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12545           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12546
12547         /* don't scroll over playfield boundaries */
12548         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12549           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12550
12551         /* don't scroll more than one field at a time */
12552         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12553
12554         /* don't scroll against the player's moving direction */
12555         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12556             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12557           scroll_y = old_scroll_y;
12558       }
12559     }
12560
12561     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12562     {
12563       if (!network.enabled && game.centered_player_nr == -1 &&
12564           !AllPlayersInVisibleScreen())
12565       {
12566         scroll_x = old_scroll_x;
12567         scroll_y = old_scroll_y;
12568       }
12569       else
12570       {
12571         ScrollScreen(player, SCROLL_INIT);
12572         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12573       }
12574     }
12575   }
12576
12577   player->StepFrame = 0;
12578
12579   if (moved & MP_MOVING)
12580   {
12581     if (old_jx != jx && old_jy == jy)
12582       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12583     else if (old_jx == jx && old_jy != jy)
12584       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12585
12586     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12587
12588     player->last_move_dir = player->MovDir;
12589     player->is_moving = TRUE;
12590     player->is_snapping = FALSE;
12591     player->is_switching = FALSE;
12592     player->is_dropping = FALSE;
12593     player->is_dropping_pressed = FALSE;
12594     player->drop_pressed_delay = 0;
12595
12596 #if 0
12597     /* should better be called here than above, but this breaks some tapes */
12598     ScrollPlayer(player, SCROLL_INIT);
12599 #endif
12600   }
12601   else
12602   {
12603     CheckGravityMovementWhenNotMoving(player);
12604
12605     player->is_moving = FALSE;
12606
12607     /* at this point, the player is allowed to move, but cannot move right now
12608        (e.g. because of something blocking the way) -- ensure that the player
12609        is also allowed to move in the next frame (in old versions before 3.1.1,
12610        the player was forced to wait again for eight frames before next try) */
12611
12612     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12613       player->move_delay = 0;   /* allow direct movement in the next frame */
12614   }
12615
12616   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12617     player->move_delay = player->move_delay_value;
12618
12619   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12620   {
12621     TestIfPlayerTouchesBadThing(jx, jy);
12622     TestIfPlayerTouchesCustomElement(jx, jy);
12623   }
12624
12625   if (!player->active)
12626     RemovePlayer(player);
12627
12628   return moved;
12629 }
12630
12631 void ScrollPlayer(struct PlayerInfo *player, int mode)
12632 {
12633   int jx = player->jx, jy = player->jy;
12634   int last_jx = player->last_jx, last_jy = player->last_jy;
12635   int move_stepsize = TILEX / player->move_delay_value;
12636
12637   if (!player->active)
12638     return;
12639
12640   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12641     return;
12642
12643   if (mode == SCROLL_INIT)
12644   {
12645     player->actual_frame_counter = FrameCounter;
12646     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12647
12648     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12649         Feld[last_jx][last_jy] == EL_EMPTY)
12650     {
12651       int last_field_block_delay = 0;   /* start with no blocking at all */
12652       int block_delay_adjustment = player->block_delay_adjustment;
12653
12654       /* if player blocks last field, add delay for exactly one move */
12655       if (player->block_last_field)
12656       {
12657         last_field_block_delay += player->move_delay_value;
12658
12659         /* when blocking enabled, prevent moving up despite gravity */
12660         if (player->gravity && player->MovDir == MV_UP)
12661           block_delay_adjustment = -1;
12662       }
12663
12664       /* add block delay adjustment (also possible when not blocking) */
12665       last_field_block_delay += block_delay_adjustment;
12666
12667       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12668       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12669     }
12670
12671     if (player->MovPos != 0)    /* player has not yet reached destination */
12672       return;
12673   }
12674   else if (!FrameReached(&player->actual_frame_counter, 1))
12675     return;
12676
12677   if (player->MovPos != 0)
12678   {
12679     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12680     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12681
12682     /* before DrawPlayer() to draw correct player graphic for this case */
12683     if (player->MovPos == 0)
12684       CheckGravityMovement(player);
12685   }
12686
12687   if (player->MovPos == 0)      /* player reached destination field */
12688   {
12689     if (player->move_delay_reset_counter > 0)
12690     {
12691       player->move_delay_reset_counter--;
12692
12693       if (player->move_delay_reset_counter == 0)
12694       {
12695         /* continue with normal speed after quickly moving through gate */
12696         HALVE_PLAYER_SPEED(player);
12697
12698         /* be able to make the next move without delay */
12699         player->move_delay = 0;
12700       }
12701     }
12702
12703     player->last_jx = jx;
12704     player->last_jy = jy;
12705
12706     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12707         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12708         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12709         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12710         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12711         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12712         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12713         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12714     {
12715       DrawPlayer(player);       /* needed here only to cleanup last field */
12716       RemovePlayer(player);
12717
12718       if (local_player->friends_still_needed == 0 ||
12719           IS_SP_ELEMENT(Feld[jx][jy]))
12720         PlayerWins(player);
12721     }
12722
12723     /* this breaks one level: "machine", level 000 */
12724     {
12725       int move_direction = player->MovDir;
12726       int enter_side = MV_DIR_OPPOSITE(move_direction);
12727       int leave_side = move_direction;
12728       int old_jx = last_jx;
12729       int old_jy = last_jy;
12730       int old_element = Feld[old_jx][old_jy];
12731       int new_element = Feld[jx][jy];
12732
12733       if (IS_CUSTOM_ELEMENT(old_element))
12734         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12735                                    CE_LEFT_BY_PLAYER,
12736                                    player->index_bit, leave_side);
12737
12738       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12739                                           CE_PLAYER_LEAVES_X,
12740                                           player->index_bit, leave_side);
12741
12742       if (IS_CUSTOM_ELEMENT(new_element))
12743         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12744                                    player->index_bit, enter_side);
12745
12746       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12747                                           CE_PLAYER_ENTERS_X,
12748                                           player->index_bit, enter_side);
12749
12750       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12751                                         CE_MOVE_OF_X, move_direction);
12752     }
12753
12754     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12755     {
12756       TestIfPlayerTouchesBadThing(jx, jy);
12757       TestIfPlayerTouchesCustomElement(jx, jy);
12758
12759       /* needed because pushed element has not yet reached its destination,
12760          so it would trigger a change event at its previous field location */
12761       if (!player->is_pushing)
12762         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12763
12764       if (!player->active)
12765         RemovePlayer(player);
12766     }
12767
12768     if (!local_player->LevelSolved && level.use_step_counter)
12769     {
12770       int i;
12771
12772       TimePlayed++;
12773
12774       if (TimeLeft > 0)
12775       {
12776         TimeLeft--;
12777
12778         if (TimeLeft <= 10 && setup.time_limit)
12779           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12780
12781         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12782
12783         DisplayGameControlValues();
12784
12785         if (!TimeLeft && setup.time_limit)
12786           for (i = 0; i < MAX_PLAYERS; i++)
12787             KillPlayer(&stored_player[i]);
12788       }
12789       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12790       {
12791         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12792
12793         DisplayGameControlValues();
12794       }
12795     }
12796
12797     if (tape.single_step && tape.recording && !tape.pausing &&
12798         !player->programmed_action)
12799       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12800
12801     if (!player->programmed_action)
12802       CheckSaveEngineSnapshot(player);
12803   }
12804 }
12805
12806 void ScrollScreen(struct PlayerInfo *player, int mode)
12807 {
12808   static unsigned int screen_frame_counter = 0;
12809
12810   if (mode == SCROLL_INIT)
12811   {
12812     /* set scrolling step size according to actual player's moving speed */
12813     ScrollStepSize = TILEX / player->move_delay_value;
12814
12815     screen_frame_counter = FrameCounter;
12816     ScreenMovDir = player->MovDir;
12817     ScreenMovPos = player->MovPos;
12818     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12819     return;
12820   }
12821   else if (!FrameReached(&screen_frame_counter, 1))
12822     return;
12823
12824   if (ScreenMovPos)
12825   {
12826     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12827     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12828     redraw_mask |= REDRAW_FIELD;
12829   }
12830   else
12831     ScreenMovDir = MV_NONE;
12832 }
12833
12834 void TestIfPlayerTouchesCustomElement(int x, int y)
12835 {
12836   static int xy[4][2] =
12837   {
12838     { 0, -1 },
12839     { -1, 0 },
12840     { +1, 0 },
12841     { 0, +1 }
12842   };
12843   static int trigger_sides[4][2] =
12844   {
12845     /* center side       border side */
12846     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12847     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12848     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12849     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12850   };
12851   static int touch_dir[4] =
12852   {
12853     MV_LEFT | MV_RIGHT,
12854     MV_UP   | MV_DOWN,
12855     MV_UP   | MV_DOWN,
12856     MV_LEFT | MV_RIGHT
12857   };
12858   int center_element = Feld[x][y];      /* should always be non-moving! */
12859   int i;
12860
12861   for (i = 0; i < NUM_DIRECTIONS; i++)
12862   {
12863     int xx = x + xy[i][0];
12864     int yy = y + xy[i][1];
12865     int center_side = trigger_sides[i][0];
12866     int border_side = trigger_sides[i][1];
12867     int border_element;
12868
12869     if (!IN_LEV_FIELD(xx, yy))
12870       continue;
12871
12872     if (IS_PLAYER(x, y))                /* player found at center element */
12873     {
12874       struct PlayerInfo *player = PLAYERINFO(x, y);
12875
12876       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12877         border_element = Feld[xx][yy];          /* may be moving! */
12878       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12879         border_element = Feld[xx][yy];
12880       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12881         border_element = MovingOrBlocked2Element(xx, yy);
12882       else
12883         continue;               /* center and border element do not touch */
12884
12885       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12886                                  player->index_bit, border_side);
12887       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12888                                           CE_PLAYER_TOUCHES_X,
12889                                           player->index_bit, border_side);
12890
12891       {
12892         /* use player element that is initially defined in the level playfield,
12893            not the player element that corresponds to the runtime player number
12894            (example: a level that contains EL_PLAYER_3 as the only player would
12895            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12896         int player_element = PLAYERINFO(x, y)->initial_element;
12897
12898         CheckElementChangeBySide(xx, yy, border_element, player_element,
12899                                  CE_TOUCHING_X, border_side);
12900       }
12901     }
12902     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12903     {
12904       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12905
12906       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12907       {
12908         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12909           continue;             /* center and border element do not touch */
12910       }
12911
12912       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12913                                  player->index_bit, center_side);
12914       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12915                                           CE_PLAYER_TOUCHES_X,
12916                                           player->index_bit, center_side);
12917
12918       {
12919         /* use player element that is initially defined in the level playfield,
12920            not the player element that corresponds to the runtime player number
12921            (example: a level that contains EL_PLAYER_3 as the only player would
12922            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12923         int player_element = PLAYERINFO(xx, yy)->initial_element;
12924
12925         CheckElementChangeBySide(x, y, center_element, player_element,
12926                                  CE_TOUCHING_X, center_side);
12927       }
12928
12929       break;
12930     }
12931   }
12932 }
12933
12934 void TestIfElementTouchesCustomElement(int x, int y)
12935 {
12936   static int xy[4][2] =
12937   {
12938     { 0, -1 },
12939     { -1, 0 },
12940     { +1, 0 },
12941     { 0, +1 }
12942   };
12943   static int trigger_sides[4][2] =
12944   {
12945     /* center side      border side */
12946     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12947     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12948     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12949     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12950   };
12951   static int touch_dir[4] =
12952   {
12953     MV_LEFT | MV_RIGHT,
12954     MV_UP   | MV_DOWN,
12955     MV_UP   | MV_DOWN,
12956     MV_LEFT | MV_RIGHT
12957   };
12958   boolean change_center_element = FALSE;
12959   int center_element = Feld[x][y];      /* should always be non-moving! */
12960   int border_element_old[NUM_DIRECTIONS];
12961   int i;
12962
12963   for (i = 0; i < NUM_DIRECTIONS; i++)
12964   {
12965     int xx = x + xy[i][0];
12966     int yy = y + xy[i][1];
12967     int border_element;
12968
12969     border_element_old[i] = -1;
12970
12971     if (!IN_LEV_FIELD(xx, yy))
12972       continue;
12973
12974     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12975       border_element = Feld[xx][yy];    /* may be moving! */
12976     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12977       border_element = Feld[xx][yy];
12978     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12979       border_element = MovingOrBlocked2Element(xx, yy);
12980     else
12981       continue;                 /* center and border element do not touch */
12982
12983     border_element_old[i] = border_element;
12984   }
12985
12986   for (i = 0; i < NUM_DIRECTIONS; i++)
12987   {
12988     int xx = x + xy[i][0];
12989     int yy = y + xy[i][1];
12990     int center_side = trigger_sides[i][0];
12991     int border_element = border_element_old[i];
12992
12993     if (border_element == -1)
12994       continue;
12995
12996     /* check for change of border element */
12997     CheckElementChangeBySide(xx, yy, border_element, center_element,
12998                              CE_TOUCHING_X, center_side);
12999
13000     /* (center element cannot be player, so we dont have to check this here) */
13001   }
13002
13003   for (i = 0; i < NUM_DIRECTIONS; i++)
13004   {
13005     int xx = x + xy[i][0];
13006     int yy = y + xy[i][1];
13007     int border_side = trigger_sides[i][1];
13008     int border_element = border_element_old[i];
13009
13010     if (border_element == -1)
13011       continue;
13012
13013     /* check for change of center element (but change it only once) */
13014     if (!change_center_element)
13015       change_center_element =
13016         CheckElementChangeBySide(x, y, center_element, border_element,
13017                                  CE_TOUCHING_X, border_side);
13018
13019     if (IS_PLAYER(xx, yy))
13020     {
13021       /* use player element that is initially defined in the level playfield,
13022          not the player element that corresponds to the runtime player number
13023          (example: a level that contains EL_PLAYER_3 as the only player would
13024          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13025       int player_element = PLAYERINFO(xx, yy)->initial_element;
13026
13027       CheckElementChangeBySide(x, y, center_element, player_element,
13028                                CE_TOUCHING_X, border_side);
13029     }
13030   }
13031 }
13032
13033 void TestIfElementHitsCustomElement(int x, int y, int direction)
13034 {
13035   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13036   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13037   int hitx = x + dx, hity = y + dy;
13038   int hitting_element = Feld[x][y];
13039   int touched_element;
13040
13041   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13042     return;
13043
13044   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13045                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13046
13047   if (IN_LEV_FIELD(hitx, hity))
13048   {
13049     int opposite_direction = MV_DIR_OPPOSITE(direction);
13050     int hitting_side = direction;
13051     int touched_side = opposite_direction;
13052     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13053                           MovDir[hitx][hity] != direction ||
13054                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13055
13056     object_hit = TRUE;
13057
13058     if (object_hit)
13059     {
13060       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13061                                CE_HITTING_X, touched_side);
13062
13063       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13064                                CE_HIT_BY_X, hitting_side);
13065
13066       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13067                                CE_HIT_BY_SOMETHING, opposite_direction);
13068
13069       if (IS_PLAYER(hitx, hity))
13070       {
13071         /* use player element that is initially defined in the level playfield,
13072            not the player element that corresponds to the runtime player number
13073            (example: a level that contains EL_PLAYER_3 as the only player would
13074            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13075         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13076
13077         CheckElementChangeBySide(x, y, hitting_element, player_element,
13078                                  CE_HITTING_X, touched_side);
13079       }
13080     }
13081   }
13082
13083   /* "hitting something" is also true when hitting the playfield border */
13084   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13085                            CE_HITTING_SOMETHING, direction);
13086 }
13087
13088 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13089 {
13090   int i, kill_x = -1, kill_y = -1;
13091
13092   int bad_element = -1;
13093   static int test_xy[4][2] =
13094   {
13095     { 0, -1 },
13096     { -1, 0 },
13097     { +1, 0 },
13098     { 0, +1 }
13099   };
13100   static int test_dir[4] =
13101   {
13102     MV_UP,
13103     MV_LEFT,
13104     MV_RIGHT,
13105     MV_DOWN
13106   };
13107
13108   for (i = 0; i < NUM_DIRECTIONS; i++)
13109   {
13110     int test_x, test_y, test_move_dir, test_element;
13111
13112     test_x = good_x + test_xy[i][0];
13113     test_y = good_y + test_xy[i][1];
13114
13115     if (!IN_LEV_FIELD(test_x, test_y))
13116       continue;
13117
13118     test_move_dir =
13119       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13120
13121     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13122
13123     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13124        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13125     */
13126     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13127         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13128     {
13129       kill_x = test_x;
13130       kill_y = test_y;
13131       bad_element = test_element;
13132
13133       break;
13134     }
13135   }
13136
13137   if (kill_x != -1 || kill_y != -1)
13138   {
13139     if (IS_PLAYER(good_x, good_y))
13140     {
13141       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13142
13143       if (player->shield_deadly_time_left > 0 &&
13144           !IS_INDESTRUCTIBLE(bad_element))
13145         Bang(kill_x, kill_y);
13146       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13147         KillPlayer(player);
13148     }
13149     else
13150       Bang(good_x, good_y);
13151   }
13152 }
13153
13154 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13155 {
13156   int i, kill_x = -1, kill_y = -1;
13157   int bad_element = Feld[bad_x][bad_y];
13158   static int test_xy[4][2] =
13159   {
13160     { 0, -1 },
13161     { -1, 0 },
13162     { +1, 0 },
13163     { 0, +1 }
13164   };
13165   static int touch_dir[4] =
13166   {
13167     MV_LEFT | MV_RIGHT,
13168     MV_UP   | MV_DOWN,
13169     MV_UP   | MV_DOWN,
13170     MV_LEFT | MV_RIGHT
13171   };
13172   static int test_dir[4] =
13173   {
13174     MV_UP,
13175     MV_LEFT,
13176     MV_RIGHT,
13177     MV_DOWN
13178   };
13179
13180   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13181     return;
13182
13183   for (i = 0; i < NUM_DIRECTIONS; i++)
13184   {
13185     int test_x, test_y, test_move_dir, test_element;
13186
13187     test_x = bad_x + test_xy[i][0];
13188     test_y = bad_y + test_xy[i][1];
13189
13190     if (!IN_LEV_FIELD(test_x, test_y))
13191       continue;
13192
13193     test_move_dir =
13194       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13195
13196     test_element = Feld[test_x][test_y];
13197
13198     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13199        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13200     */
13201     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13202         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13203     {
13204       /* good thing is player or penguin that does not move away */
13205       if (IS_PLAYER(test_x, test_y))
13206       {
13207         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13208
13209         if (bad_element == EL_ROBOT && player->is_moving)
13210           continue;     /* robot does not kill player if he is moving */
13211
13212         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13213         {
13214           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13215             continue;           /* center and border element do not touch */
13216         }
13217
13218         kill_x = test_x;
13219         kill_y = test_y;
13220
13221         break;
13222       }
13223       else if (test_element == EL_PENGUIN)
13224       {
13225         kill_x = test_x;
13226         kill_y = test_y;
13227
13228         break;
13229       }
13230     }
13231   }
13232
13233   if (kill_x != -1 || kill_y != -1)
13234   {
13235     if (IS_PLAYER(kill_x, kill_y))
13236     {
13237       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13238
13239       if (player->shield_deadly_time_left > 0 &&
13240           !IS_INDESTRUCTIBLE(bad_element))
13241         Bang(bad_x, bad_y);
13242       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13243         KillPlayer(player);
13244     }
13245     else
13246       Bang(kill_x, kill_y);
13247   }
13248 }
13249
13250 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13251 {
13252   int bad_element = Feld[bad_x][bad_y];
13253   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13254   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13255   int test_x = bad_x + dx, test_y = bad_y + dy;
13256   int test_move_dir, test_element;
13257   int kill_x = -1, kill_y = -1;
13258
13259   if (!IN_LEV_FIELD(test_x, test_y))
13260     return;
13261
13262   test_move_dir =
13263     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13264
13265   test_element = Feld[test_x][test_y];
13266
13267   if (test_move_dir != bad_move_dir)
13268   {
13269     /* good thing can be player or penguin that does not move away */
13270     if (IS_PLAYER(test_x, test_y))
13271     {
13272       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13273
13274       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13275          player as being hit when he is moving towards the bad thing, because
13276          the "get hit by" condition would be lost after the player stops) */
13277       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13278         return;         /* player moves away from bad thing */
13279
13280       kill_x = test_x;
13281       kill_y = test_y;
13282     }
13283     else if (test_element == EL_PENGUIN)
13284     {
13285       kill_x = test_x;
13286       kill_y = test_y;
13287     }
13288   }
13289
13290   if (kill_x != -1 || kill_y != -1)
13291   {
13292     if (IS_PLAYER(kill_x, kill_y))
13293     {
13294       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13295
13296       if (player->shield_deadly_time_left > 0 &&
13297           !IS_INDESTRUCTIBLE(bad_element))
13298         Bang(bad_x, bad_y);
13299       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13300         KillPlayer(player);
13301     }
13302     else
13303       Bang(kill_x, kill_y);
13304   }
13305 }
13306
13307 void TestIfPlayerTouchesBadThing(int x, int y)
13308 {
13309   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13310 }
13311
13312 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13313 {
13314   TestIfGoodThingHitsBadThing(x, y, move_dir);
13315 }
13316
13317 void TestIfBadThingTouchesPlayer(int x, int y)
13318 {
13319   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13320 }
13321
13322 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13323 {
13324   TestIfBadThingHitsGoodThing(x, y, move_dir);
13325 }
13326
13327 void TestIfFriendTouchesBadThing(int x, int y)
13328 {
13329   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13330 }
13331
13332 void TestIfBadThingTouchesFriend(int x, int y)
13333 {
13334   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13335 }
13336
13337 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13338 {
13339   int i, kill_x = bad_x, kill_y = bad_y;
13340   static int xy[4][2] =
13341   {
13342     { 0, -1 },
13343     { -1, 0 },
13344     { +1, 0 },
13345     { 0, +1 }
13346   };
13347
13348   for (i = 0; i < NUM_DIRECTIONS; i++)
13349   {
13350     int x, y, element;
13351
13352     x = bad_x + xy[i][0];
13353     y = bad_y + xy[i][1];
13354     if (!IN_LEV_FIELD(x, y))
13355       continue;
13356
13357     element = Feld[x][y];
13358     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13359         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13360     {
13361       kill_x = x;
13362       kill_y = y;
13363       break;
13364     }
13365   }
13366
13367   if (kill_x != bad_x || kill_y != bad_y)
13368     Bang(bad_x, bad_y);
13369 }
13370
13371 void KillPlayer(struct PlayerInfo *player)
13372 {
13373   int jx = player->jx, jy = player->jy;
13374
13375   if (!player->active)
13376     return;
13377
13378 #if 0
13379   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13380          player->killed, player->active, player->reanimated);
13381 #endif
13382
13383   /* the following code was introduced to prevent an infinite loop when calling
13384      -> Bang()
13385      -> CheckTriggeredElementChangeExt()
13386      -> ExecuteCustomElementAction()
13387      -> KillPlayer()
13388      -> (infinitely repeating the above sequence of function calls)
13389      which occurs when killing the player while having a CE with the setting
13390      "kill player X when explosion of <player X>"; the solution using a new
13391      field "player->killed" was chosen for backwards compatibility, although
13392      clever use of the fields "player->active" etc. would probably also work */
13393 #if 1
13394   if (player->killed)
13395     return;
13396 #endif
13397
13398   player->killed = TRUE;
13399
13400   /* remove accessible field at the player's position */
13401   Feld[jx][jy] = EL_EMPTY;
13402
13403   /* deactivate shield (else Bang()/Explode() would not work right) */
13404   player->shield_normal_time_left = 0;
13405   player->shield_deadly_time_left = 0;
13406
13407 #if 0
13408   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13409          player->killed, player->active, player->reanimated);
13410 #endif
13411
13412   Bang(jx, jy);
13413
13414 #if 0
13415   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13416          player->killed, player->active, player->reanimated);
13417 #endif
13418
13419   if (player->reanimated)       /* killed player may have been reanimated */
13420     player->killed = player->reanimated = FALSE;
13421   else
13422     BuryPlayer(player);
13423 }
13424
13425 static void KillPlayerUnlessEnemyProtected(int x, int y)
13426 {
13427   if (!PLAYER_ENEMY_PROTECTED(x, y))
13428     KillPlayer(PLAYERINFO(x, y));
13429 }
13430
13431 static void KillPlayerUnlessExplosionProtected(int x, int y)
13432 {
13433   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13434     KillPlayer(PLAYERINFO(x, y));
13435 }
13436
13437 void BuryPlayer(struct PlayerInfo *player)
13438 {
13439   int jx = player->jx, jy = player->jy;
13440
13441   if (!player->active)
13442     return;
13443
13444   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13445   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13446
13447   player->GameOver = TRUE;
13448   RemovePlayer(player);
13449 }
13450
13451 void RemovePlayer(struct PlayerInfo *player)
13452 {
13453   int jx = player->jx, jy = player->jy;
13454   int i, found = FALSE;
13455
13456   player->present = FALSE;
13457   player->active = FALSE;
13458
13459   if (!ExplodeField[jx][jy])
13460     StorePlayer[jx][jy] = 0;
13461
13462   if (player->is_moving)
13463     TEST_DrawLevelField(player->last_jx, player->last_jy);
13464
13465   for (i = 0; i < MAX_PLAYERS; i++)
13466     if (stored_player[i].active)
13467       found = TRUE;
13468
13469   if (!found)
13470     AllPlayersGone = TRUE;
13471
13472   ExitX = ZX = jx;
13473   ExitY = ZY = jy;
13474 }
13475
13476 static void setFieldForSnapping(int x, int y, int element, int direction)
13477 {
13478   struct ElementInfo *ei = &element_info[element];
13479   int direction_bit = MV_DIR_TO_BIT(direction);
13480   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13481   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13482                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13483
13484   Feld[x][y] = EL_ELEMENT_SNAPPING;
13485   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13486
13487   ResetGfxAnimation(x, y);
13488
13489   GfxElement[x][y] = element;
13490   GfxAction[x][y] = action;
13491   GfxDir[x][y] = direction;
13492   GfxFrame[x][y] = -1;
13493 }
13494
13495 /*
13496   =============================================================================
13497   checkDiagonalPushing()
13498   -----------------------------------------------------------------------------
13499   check if diagonal input device direction results in pushing of object
13500   (by checking if the alternative direction is walkable, diggable, ...)
13501   =============================================================================
13502 */
13503
13504 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13505                                     int x, int y, int real_dx, int real_dy)
13506 {
13507   int jx, jy, dx, dy, xx, yy;
13508
13509   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13510     return TRUE;
13511
13512   /* diagonal direction: check alternative direction */
13513   jx = player->jx;
13514   jy = player->jy;
13515   dx = x - jx;
13516   dy = y - jy;
13517   xx = jx + (dx == 0 ? real_dx : 0);
13518   yy = jy + (dy == 0 ? real_dy : 0);
13519
13520   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13521 }
13522
13523 /*
13524   =============================================================================
13525   DigField()
13526   -----------------------------------------------------------------------------
13527   x, y:                 field next to player (non-diagonal) to try to dig to
13528   real_dx, real_dy:     direction as read from input device (can be diagonal)
13529   =============================================================================
13530 */
13531
13532 static int DigField(struct PlayerInfo *player,
13533                     int oldx, int oldy, int x, int y,
13534                     int real_dx, int real_dy, int mode)
13535 {
13536   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13537   boolean player_was_pushing = player->is_pushing;
13538   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13539   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13540   int jx = oldx, jy = oldy;
13541   int dx = x - jx, dy = y - jy;
13542   int nextx = x + dx, nexty = y + dy;
13543   int move_direction = (dx == -1 ? MV_LEFT  :
13544                         dx == +1 ? MV_RIGHT :
13545                         dy == -1 ? MV_UP    :
13546                         dy == +1 ? MV_DOWN  : MV_NONE);
13547   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13548   int dig_side = MV_DIR_OPPOSITE(move_direction);
13549   int old_element = Feld[jx][jy];
13550   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13551   int collect_count;
13552
13553   if (is_player)                /* function can also be called by EL_PENGUIN */
13554   {
13555     if (player->MovPos == 0)
13556     {
13557       player->is_digging = FALSE;
13558       player->is_collecting = FALSE;
13559     }
13560
13561     if (player->MovPos == 0)    /* last pushing move finished */
13562       player->is_pushing = FALSE;
13563
13564     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13565     {
13566       player->is_switching = FALSE;
13567       player->push_delay = -1;
13568
13569       return MP_NO_ACTION;
13570     }
13571   }
13572
13573   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13574     old_element = Back[jx][jy];
13575
13576   /* in case of element dropped at player position, check background */
13577   else if (Back[jx][jy] != EL_EMPTY &&
13578            game.engine_version >= VERSION_IDENT(2,2,0,0))
13579     old_element = Back[jx][jy];
13580
13581   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13582     return MP_NO_ACTION;        /* field has no opening in this direction */
13583
13584   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13585     return MP_NO_ACTION;        /* field has no opening in this direction */
13586
13587   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13588   {
13589     SplashAcid(x, y);
13590
13591     Feld[jx][jy] = player->artwork_element;
13592     InitMovingField(jx, jy, MV_DOWN);
13593     Store[jx][jy] = EL_ACID;
13594     ContinueMoving(jx, jy);
13595     BuryPlayer(player);
13596
13597     return MP_DONT_RUN_INTO;
13598   }
13599
13600   if (player_can_move && DONT_RUN_INTO(element))
13601   {
13602     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13603
13604     return MP_DONT_RUN_INTO;
13605   }
13606
13607   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13608     return MP_NO_ACTION;
13609
13610   collect_count = element_info[element].collect_count_initial;
13611
13612   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13613     return MP_NO_ACTION;
13614
13615   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13616     player_can_move = player_can_move_or_snap;
13617
13618   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13619       game.engine_version >= VERSION_IDENT(2,2,0,0))
13620   {
13621     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13622                                player->index_bit, dig_side);
13623     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13624                                         player->index_bit, dig_side);
13625
13626     if (element == EL_DC_LANDMINE)
13627       Bang(x, y);
13628
13629     if (Feld[x][y] != element)          /* field changed by snapping */
13630       return MP_ACTION;
13631
13632     return MP_NO_ACTION;
13633   }
13634
13635   if (player->gravity && is_player && !player->is_auto_moving &&
13636       canFallDown(player) && move_direction != MV_DOWN &&
13637       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13638     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13639
13640   if (player_can_move &&
13641       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13642   {
13643     int sound_element = SND_ELEMENT(element);
13644     int sound_action = ACTION_WALKING;
13645
13646     if (IS_RND_GATE(element))
13647     {
13648       if (!player->key[RND_GATE_NR(element)])
13649         return MP_NO_ACTION;
13650     }
13651     else if (IS_RND_GATE_GRAY(element))
13652     {
13653       if (!player->key[RND_GATE_GRAY_NR(element)])
13654         return MP_NO_ACTION;
13655     }
13656     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13657     {
13658       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13659         return MP_NO_ACTION;
13660     }
13661     else if (element == EL_EXIT_OPEN ||
13662              element == EL_EM_EXIT_OPEN ||
13663              element == EL_EM_EXIT_OPENING ||
13664              element == EL_STEEL_EXIT_OPEN ||
13665              element == EL_EM_STEEL_EXIT_OPEN ||
13666              element == EL_EM_STEEL_EXIT_OPENING ||
13667              element == EL_SP_EXIT_OPEN ||
13668              element == EL_SP_EXIT_OPENING)
13669     {
13670       sound_action = ACTION_PASSING;    /* player is passing exit */
13671     }
13672     else if (element == EL_EMPTY)
13673     {
13674       sound_action = ACTION_MOVING;             /* nothing to walk on */
13675     }
13676
13677     /* play sound from background or player, whatever is available */
13678     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13679       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13680     else
13681       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13682   }
13683   else if (player_can_move &&
13684            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13685   {
13686     if (!ACCESS_FROM(element, opposite_direction))
13687       return MP_NO_ACTION;      /* field not accessible from this direction */
13688
13689     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13690       return MP_NO_ACTION;
13691
13692     if (IS_EM_GATE(element))
13693     {
13694       if (!player->key[EM_GATE_NR(element)])
13695         return MP_NO_ACTION;
13696     }
13697     else if (IS_EM_GATE_GRAY(element))
13698     {
13699       if (!player->key[EM_GATE_GRAY_NR(element)])
13700         return MP_NO_ACTION;
13701     }
13702     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13703     {
13704       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13705         return MP_NO_ACTION;
13706     }
13707     else if (IS_EMC_GATE(element))
13708     {
13709       if (!player->key[EMC_GATE_NR(element)])
13710         return MP_NO_ACTION;
13711     }
13712     else if (IS_EMC_GATE_GRAY(element))
13713     {
13714       if (!player->key[EMC_GATE_GRAY_NR(element)])
13715         return MP_NO_ACTION;
13716     }
13717     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13718     {
13719       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13720         return MP_NO_ACTION;
13721     }
13722     else if (element == EL_DC_GATE_WHITE ||
13723              element == EL_DC_GATE_WHITE_GRAY ||
13724              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13725     {
13726       if (player->num_white_keys == 0)
13727         return MP_NO_ACTION;
13728
13729       player->num_white_keys--;
13730     }
13731     else if (IS_SP_PORT(element))
13732     {
13733       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13734           element == EL_SP_GRAVITY_PORT_RIGHT ||
13735           element == EL_SP_GRAVITY_PORT_UP ||
13736           element == EL_SP_GRAVITY_PORT_DOWN)
13737         player->gravity = !player->gravity;
13738       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13739                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13740                element == EL_SP_GRAVITY_ON_PORT_UP ||
13741                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13742         player->gravity = TRUE;
13743       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13744                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13745                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13746                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13747         player->gravity = FALSE;
13748     }
13749
13750     /* automatically move to the next field with double speed */
13751     player->programmed_action = move_direction;
13752
13753     if (player->move_delay_reset_counter == 0)
13754     {
13755       player->move_delay_reset_counter = 2;     /* two double speed steps */
13756
13757       DOUBLE_PLAYER_SPEED(player);
13758     }
13759
13760     PlayLevelSoundAction(x, y, ACTION_PASSING);
13761   }
13762   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13763   {
13764     RemoveField(x, y);
13765
13766     if (mode != DF_SNAP)
13767     {
13768       GfxElement[x][y] = GFX_ELEMENT(element);
13769       player->is_digging = TRUE;
13770     }
13771
13772     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13773
13774     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13775                                         player->index_bit, dig_side);
13776
13777     if (mode == DF_SNAP)
13778     {
13779       if (level.block_snap_field)
13780         setFieldForSnapping(x, y, element, move_direction);
13781       else
13782         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13783
13784       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13785                                           player->index_bit, dig_side);
13786     }
13787   }
13788   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13789   {
13790     RemoveField(x, y);
13791
13792     if (is_player && mode != DF_SNAP)
13793     {
13794       GfxElement[x][y] = element;
13795       player->is_collecting = TRUE;
13796     }
13797
13798     if (element == EL_SPEED_PILL)
13799     {
13800       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13801     }
13802     else if (element == EL_EXTRA_TIME && level.time > 0)
13803     {
13804       TimeLeft += level.extra_time;
13805
13806       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13807
13808       DisplayGameControlValues();
13809     }
13810     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13811     {
13812       player->shield_normal_time_left += level.shield_normal_time;
13813       if (element == EL_SHIELD_DEADLY)
13814         player->shield_deadly_time_left += level.shield_deadly_time;
13815     }
13816     else if (element == EL_DYNAMITE ||
13817              element == EL_EM_DYNAMITE ||
13818              element == EL_SP_DISK_RED)
13819     {
13820       if (player->inventory_size < MAX_INVENTORY_SIZE)
13821         player->inventory_element[player->inventory_size++] = element;
13822
13823       DrawGameDoorValues();
13824     }
13825     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13826     {
13827       player->dynabomb_count++;
13828       player->dynabombs_left++;
13829     }
13830     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13831     {
13832       player->dynabomb_size++;
13833     }
13834     else if (element == EL_DYNABOMB_INCREASE_POWER)
13835     {
13836       player->dynabomb_xl = TRUE;
13837     }
13838     else if (IS_KEY(element))
13839     {
13840       player->key[KEY_NR(element)] = TRUE;
13841
13842       DrawGameDoorValues();
13843     }
13844     else if (element == EL_DC_KEY_WHITE)
13845     {
13846       player->num_white_keys++;
13847
13848       /* display white keys? */
13849       /* DrawGameDoorValues(); */
13850     }
13851     else if (IS_ENVELOPE(element))
13852     {
13853       player->show_envelope = element;
13854     }
13855     else if (element == EL_EMC_LENSES)
13856     {
13857       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13858
13859       RedrawAllInvisibleElementsForLenses();
13860     }
13861     else if (element == EL_EMC_MAGNIFIER)
13862     {
13863       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13864
13865       RedrawAllInvisibleElementsForMagnifier();
13866     }
13867     else if (IS_DROPPABLE(element) ||
13868              IS_THROWABLE(element))     /* can be collected and dropped */
13869     {
13870       int i;
13871
13872       if (collect_count == 0)
13873         player->inventory_infinite_element = element;
13874       else
13875         for (i = 0; i < collect_count; i++)
13876           if (player->inventory_size < MAX_INVENTORY_SIZE)
13877             player->inventory_element[player->inventory_size++] = element;
13878
13879       DrawGameDoorValues();
13880     }
13881     else if (collect_count > 0)
13882     {
13883       local_player->gems_still_needed -= collect_count;
13884       if (local_player->gems_still_needed < 0)
13885         local_player->gems_still_needed = 0;
13886
13887       game.snapshot.collected_item = TRUE;
13888
13889       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13890
13891       DisplayGameControlValues();
13892     }
13893
13894     RaiseScoreElement(element);
13895     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13896
13897     if (is_player)
13898       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13899                                           player->index_bit, dig_side);
13900
13901     if (mode == DF_SNAP)
13902     {
13903       if (level.block_snap_field)
13904         setFieldForSnapping(x, y, element, move_direction);
13905       else
13906         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13907
13908       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13909                                           player->index_bit, dig_side);
13910     }
13911   }
13912   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13913   {
13914     if (mode == DF_SNAP && element != EL_BD_ROCK)
13915       return MP_NO_ACTION;
13916
13917     if (CAN_FALL(element) && dy)
13918       return MP_NO_ACTION;
13919
13920     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13921         !(element == EL_SPRING && level.use_spring_bug))
13922       return MP_NO_ACTION;
13923
13924     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13925         ((move_direction & MV_VERTICAL &&
13926           ((element_info[element].move_pattern & MV_LEFT &&
13927             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13928            (element_info[element].move_pattern & MV_RIGHT &&
13929             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13930          (move_direction & MV_HORIZONTAL &&
13931           ((element_info[element].move_pattern & MV_UP &&
13932             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13933            (element_info[element].move_pattern & MV_DOWN &&
13934             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13935       return MP_NO_ACTION;
13936
13937     /* do not push elements already moving away faster than player */
13938     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13939         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13940       return MP_NO_ACTION;
13941
13942     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13943     {
13944       if (player->push_delay_value == -1 || !player_was_pushing)
13945         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13946     }
13947     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13948     {
13949       if (player->push_delay_value == -1)
13950         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13951     }
13952     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13953     {
13954       if (!player->is_pushing)
13955         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13956     }
13957
13958     player->is_pushing = TRUE;
13959     player->is_active = TRUE;
13960
13961     if (!(IN_LEV_FIELD(nextx, nexty) &&
13962           (IS_FREE(nextx, nexty) ||
13963            (IS_SB_ELEMENT(element) &&
13964             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13965            (IS_CUSTOM_ELEMENT(element) &&
13966             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13967       return MP_NO_ACTION;
13968
13969     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13970       return MP_NO_ACTION;
13971
13972     if (player->push_delay == -1)       /* new pushing; restart delay */
13973       player->push_delay = 0;
13974
13975     if (player->push_delay < player->push_delay_value &&
13976         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13977         element != EL_SPRING && element != EL_BALLOON)
13978     {
13979       /* make sure that there is no move delay before next try to push */
13980       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13981         player->move_delay = 0;
13982
13983       return MP_NO_ACTION;
13984     }
13985
13986     if (IS_CUSTOM_ELEMENT(element) &&
13987         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13988     {
13989       if (!DigFieldByCE(nextx, nexty, element))
13990         return MP_NO_ACTION;
13991     }
13992
13993     if (IS_SB_ELEMENT(element))
13994     {
13995       if (element == EL_SOKOBAN_FIELD_FULL)
13996       {
13997         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13998         local_player->sokobanfields_still_needed++;
13999       }
14000
14001       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14002       {
14003         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14004         local_player->sokobanfields_still_needed--;
14005       }
14006
14007       Feld[x][y] = EL_SOKOBAN_OBJECT;
14008
14009       if (Back[x][y] == Back[nextx][nexty])
14010         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14011       else if (Back[x][y] != 0)
14012         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14013                                     ACTION_EMPTYING);
14014       else
14015         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14016                                     ACTION_FILLING);
14017
14018       if (local_player->sokobanfields_still_needed == 0 &&
14019           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14020       {
14021         PlayerWins(player);
14022
14023         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14024       }
14025     }
14026     else
14027       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14028
14029     InitMovingField(x, y, move_direction);
14030     GfxAction[x][y] = ACTION_PUSHING;
14031
14032     if (mode == DF_SNAP)
14033       ContinueMoving(x, y);
14034     else
14035       MovPos[x][y] = (dx != 0 ? dx : dy);
14036
14037     Pushed[x][y] = TRUE;
14038     Pushed[nextx][nexty] = TRUE;
14039
14040     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14041       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14042     else
14043       player->push_delay_value = -1;    /* get new value later */
14044
14045     /* check for element change _after_ element has been pushed */
14046     if (game.use_change_when_pushing_bug)
14047     {
14048       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14049                                  player->index_bit, dig_side);
14050       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14051                                           player->index_bit, dig_side);
14052     }
14053   }
14054   else if (IS_SWITCHABLE(element))
14055   {
14056     if (PLAYER_SWITCHING(player, x, y))
14057     {
14058       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14059                                           player->index_bit, dig_side);
14060
14061       return MP_ACTION;
14062     }
14063
14064     player->is_switching = TRUE;
14065     player->switch_x = x;
14066     player->switch_y = y;
14067
14068     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14069
14070     if (element == EL_ROBOT_WHEEL)
14071     {
14072       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14073       ZX = x;
14074       ZY = y;
14075
14076       game.robot_wheel_active = TRUE;
14077
14078       TEST_DrawLevelField(x, y);
14079     }
14080     else if (element == EL_SP_TERMINAL)
14081     {
14082       int xx, yy;
14083
14084       SCAN_PLAYFIELD(xx, yy)
14085       {
14086         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14087         {
14088           Bang(xx, yy);
14089         }
14090         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14091         {
14092           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14093
14094           ResetGfxAnimation(xx, yy);
14095           TEST_DrawLevelField(xx, yy);
14096         }
14097       }
14098     }
14099     else if (IS_BELT_SWITCH(element))
14100     {
14101       ToggleBeltSwitch(x, y);
14102     }
14103     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14104              element == EL_SWITCHGATE_SWITCH_DOWN ||
14105              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14106              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14107     {
14108       ToggleSwitchgateSwitch(x, y);
14109     }
14110     else if (element == EL_LIGHT_SWITCH ||
14111              element == EL_LIGHT_SWITCH_ACTIVE)
14112     {
14113       ToggleLightSwitch(x, y);
14114     }
14115     else if (element == EL_TIMEGATE_SWITCH ||
14116              element == EL_DC_TIMEGATE_SWITCH)
14117     {
14118       ActivateTimegateSwitch(x, y);
14119     }
14120     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14121              element == EL_BALLOON_SWITCH_RIGHT ||
14122              element == EL_BALLOON_SWITCH_UP    ||
14123              element == EL_BALLOON_SWITCH_DOWN  ||
14124              element == EL_BALLOON_SWITCH_NONE  ||
14125              element == EL_BALLOON_SWITCH_ANY)
14126     {
14127       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14128                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14129                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14130                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14131                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14132                              move_direction);
14133     }
14134     else if (element == EL_LAMP)
14135     {
14136       Feld[x][y] = EL_LAMP_ACTIVE;
14137       local_player->lights_still_needed--;
14138
14139       ResetGfxAnimation(x, y);
14140       TEST_DrawLevelField(x, y);
14141     }
14142     else if (element == EL_TIME_ORB_FULL)
14143     {
14144       Feld[x][y] = EL_TIME_ORB_EMPTY;
14145
14146       if (level.time > 0 || level.use_time_orb_bug)
14147       {
14148         TimeLeft += level.time_orb_time;
14149         game.no_time_limit = FALSE;
14150
14151         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14152
14153         DisplayGameControlValues();
14154       }
14155
14156       ResetGfxAnimation(x, y);
14157       TEST_DrawLevelField(x, y);
14158     }
14159     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14160              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14161     {
14162       int xx, yy;
14163
14164       game.ball_state = !game.ball_state;
14165
14166       SCAN_PLAYFIELD(xx, yy)
14167       {
14168         int e = Feld[xx][yy];
14169
14170         if (game.ball_state)
14171         {
14172           if (e == EL_EMC_MAGIC_BALL)
14173             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14174           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14175             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14176         }
14177         else
14178         {
14179           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14180             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14181           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14182             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14183         }
14184       }
14185     }
14186
14187     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14188                                         player->index_bit, dig_side);
14189
14190     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14191                                         player->index_bit, dig_side);
14192
14193     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14194                                         player->index_bit, dig_side);
14195
14196     return MP_ACTION;
14197   }
14198   else
14199   {
14200     if (!PLAYER_SWITCHING(player, x, y))
14201     {
14202       player->is_switching = TRUE;
14203       player->switch_x = x;
14204       player->switch_y = y;
14205
14206       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14207                                  player->index_bit, dig_side);
14208       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14209                                           player->index_bit, dig_side);
14210
14211       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14212                                  player->index_bit, dig_side);
14213       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14214                                           player->index_bit, dig_side);
14215     }
14216
14217     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14218                                player->index_bit, dig_side);
14219     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14220                                         player->index_bit, dig_side);
14221
14222     return MP_NO_ACTION;
14223   }
14224
14225   player->push_delay = -1;
14226
14227   if (is_player)                /* function can also be called by EL_PENGUIN */
14228   {
14229     if (Feld[x][y] != element)          /* really digged/collected something */
14230     {
14231       player->is_collecting = !player->is_digging;
14232       player->is_active = TRUE;
14233     }
14234   }
14235
14236   return MP_MOVING;
14237 }
14238
14239 static boolean DigFieldByCE(int x, int y, int digging_element)
14240 {
14241   int element = Feld[x][y];
14242
14243   if (!IS_FREE(x, y))
14244   {
14245     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14246                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14247                   ACTION_BREAKING);
14248
14249     /* no element can dig solid indestructible elements */
14250     if (IS_INDESTRUCTIBLE(element) &&
14251         !IS_DIGGABLE(element) &&
14252         !IS_COLLECTIBLE(element))
14253       return FALSE;
14254
14255     if (AmoebaNr[x][y] &&
14256         (element == EL_AMOEBA_FULL ||
14257          element == EL_BD_AMOEBA ||
14258          element == EL_AMOEBA_GROWING))
14259     {
14260       AmoebaCnt[AmoebaNr[x][y]]--;
14261       AmoebaCnt2[AmoebaNr[x][y]]--;
14262     }
14263
14264     if (IS_MOVING(x, y))
14265       RemoveMovingField(x, y);
14266     else
14267     {
14268       RemoveField(x, y);
14269       TEST_DrawLevelField(x, y);
14270     }
14271
14272     /* if digged element was about to explode, prevent the explosion */
14273     ExplodeField[x][y] = EX_TYPE_NONE;
14274
14275     PlayLevelSoundAction(x, y, action);
14276   }
14277
14278   Store[x][y] = EL_EMPTY;
14279
14280   /* this makes it possible to leave the removed element again */
14281   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14282     Store[x][y] = element;
14283
14284   return TRUE;
14285 }
14286
14287 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14288 {
14289   int jx = player->jx, jy = player->jy;
14290   int x = jx + dx, y = jy + dy;
14291   int snap_direction = (dx == -1 ? MV_LEFT  :
14292                         dx == +1 ? MV_RIGHT :
14293                         dy == -1 ? MV_UP    :
14294                         dy == +1 ? MV_DOWN  : MV_NONE);
14295   boolean can_continue_snapping = (level.continuous_snapping &&
14296                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14297
14298   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14299     return FALSE;
14300
14301   if (!player->active || !IN_LEV_FIELD(x, y))
14302     return FALSE;
14303
14304   if (dx && dy)
14305     return FALSE;
14306
14307   if (!dx && !dy)
14308   {
14309     if (player->MovPos == 0)
14310       player->is_pushing = FALSE;
14311
14312     player->is_snapping = FALSE;
14313
14314     if (player->MovPos == 0)
14315     {
14316       player->is_moving = FALSE;
14317       player->is_digging = FALSE;
14318       player->is_collecting = FALSE;
14319     }
14320
14321     return FALSE;
14322   }
14323
14324   /* prevent snapping with already pressed snap key when not allowed */
14325   if (player->is_snapping && !can_continue_snapping)
14326     return FALSE;
14327
14328   player->MovDir = snap_direction;
14329
14330   if (player->MovPos == 0)
14331   {
14332     player->is_moving = FALSE;
14333     player->is_digging = FALSE;
14334     player->is_collecting = FALSE;
14335   }
14336
14337   player->is_dropping = FALSE;
14338   player->is_dropping_pressed = FALSE;
14339   player->drop_pressed_delay = 0;
14340
14341   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14342     return FALSE;
14343
14344   player->is_snapping = TRUE;
14345   player->is_active = TRUE;
14346
14347   if (player->MovPos == 0)
14348   {
14349     player->is_moving = FALSE;
14350     player->is_digging = FALSE;
14351     player->is_collecting = FALSE;
14352   }
14353
14354   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14355     TEST_DrawLevelField(player->last_jx, player->last_jy);
14356
14357   TEST_DrawLevelField(x, y);
14358
14359   return TRUE;
14360 }
14361
14362 static boolean DropElement(struct PlayerInfo *player)
14363 {
14364   int old_element, new_element;
14365   int dropx = player->jx, dropy = player->jy;
14366   int drop_direction = player->MovDir;
14367   int drop_side = drop_direction;
14368   int drop_element = get_next_dropped_element(player);
14369
14370   /* do not drop an element on top of another element; when holding drop key
14371      pressed without moving, dropped element must move away before the next
14372      element can be dropped (this is especially important if the next element
14373      is dynamite, which can be placed on background for historical reasons) */
14374   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14375     return MP_ACTION;
14376
14377   if (IS_THROWABLE(drop_element))
14378   {
14379     dropx += GET_DX_FROM_DIR(drop_direction);
14380     dropy += GET_DY_FROM_DIR(drop_direction);
14381
14382     if (!IN_LEV_FIELD(dropx, dropy))
14383       return FALSE;
14384   }
14385
14386   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14387   new_element = drop_element;           /* default: no change when dropping */
14388
14389   /* check if player is active, not moving and ready to drop */
14390   if (!player->active || player->MovPos || player->drop_delay > 0)
14391     return FALSE;
14392
14393   /* check if player has anything that can be dropped */
14394   if (new_element == EL_UNDEFINED)
14395     return FALSE;
14396
14397   /* only set if player has anything that can be dropped */
14398   player->is_dropping_pressed = TRUE;
14399
14400   /* check if drop key was pressed long enough for EM style dynamite */
14401   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14402     return FALSE;
14403
14404   /* check if anything can be dropped at the current position */
14405   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14406     return FALSE;
14407
14408   /* collected custom elements can only be dropped on empty fields */
14409   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14410     return FALSE;
14411
14412   if (old_element != EL_EMPTY)
14413     Back[dropx][dropy] = old_element;   /* store old element on this field */
14414
14415   ResetGfxAnimation(dropx, dropy);
14416   ResetRandomAnimationValue(dropx, dropy);
14417
14418   if (player->inventory_size > 0 ||
14419       player->inventory_infinite_element != EL_UNDEFINED)
14420   {
14421     if (player->inventory_size > 0)
14422     {
14423       player->inventory_size--;
14424
14425       DrawGameDoorValues();
14426
14427       if (new_element == EL_DYNAMITE)
14428         new_element = EL_DYNAMITE_ACTIVE;
14429       else if (new_element == EL_EM_DYNAMITE)
14430         new_element = EL_EM_DYNAMITE_ACTIVE;
14431       else if (new_element == EL_SP_DISK_RED)
14432         new_element = EL_SP_DISK_RED_ACTIVE;
14433     }
14434
14435     Feld[dropx][dropy] = new_element;
14436
14437     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14438       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14439                           el2img(Feld[dropx][dropy]), 0);
14440
14441     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14442
14443     /* needed if previous element just changed to "empty" in the last frame */
14444     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14445
14446     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14447                                player->index_bit, drop_side);
14448     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14449                                         CE_PLAYER_DROPS_X,
14450                                         player->index_bit, drop_side);
14451
14452     TestIfElementTouchesCustomElement(dropx, dropy);
14453   }
14454   else          /* player is dropping a dyna bomb */
14455   {
14456     player->dynabombs_left--;
14457
14458     Feld[dropx][dropy] = new_element;
14459
14460     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14461       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14462                           el2img(Feld[dropx][dropy]), 0);
14463
14464     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14465   }
14466
14467   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14468     InitField_WithBug1(dropx, dropy, FALSE);
14469
14470   new_element = Feld[dropx][dropy];     /* element might have changed */
14471
14472   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14473       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14474   {
14475     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14476       MovDir[dropx][dropy] = drop_direction;
14477
14478     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14479
14480     /* do not cause impact style collision by dropping elements that can fall */
14481     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14482   }
14483
14484   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14485   player->is_dropping = TRUE;
14486
14487   player->drop_pressed_delay = 0;
14488   player->is_dropping_pressed = FALSE;
14489
14490   player->drop_x = dropx;
14491   player->drop_y = dropy;
14492
14493   return TRUE;
14494 }
14495
14496 /* ------------------------------------------------------------------------- */
14497 /* game sound playing functions                                              */
14498 /* ------------------------------------------------------------------------- */
14499
14500 static int *loop_sound_frame = NULL;
14501 static int *loop_sound_volume = NULL;
14502
14503 void InitPlayLevelSound()
14504 {
14505   int num_sounds = getSoundListSize();
14506
14507   checked_free(loop_sound_frame);
14508   checked_free(loop_sound_volume);
14509
14510   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14511   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14512 }
14513
14514 static void PlayLevelSound(int x, int y, int nr)
14515 {
14516   int sx = SCREENX(x), sy = SCREENY(y);
14517   int volume, stereo_position;
14518   int max_distance = 8;
14519   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14520
14521   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14522       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14523     return;
14524
14525   if (!IN_LEV_FIELD(x, y) ||
14526       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14527       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14528     return;
14529
14530   volume = SOUND_MAX_VOLUME;
14531
14532   if (!IN_SCR_FIELD(sx, sy))
14533   {
14534     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14535     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14536
14537     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14538   }
14539
14540   stereo_position = (SOUND_MAX_LEFT +
14541                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14542                      (SCR_FIELDX + 2 * max_distance));
14543
14544   if (IS_LOOP_SOUND(nr))
14545   {
14546     /* This assures that quieter loop sounds do not overwrite louder ones,
14547        while restarting sound volume comparison with each new game frame. */
14548
14549     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14550       return;
14551
14552     loop_sound_volume[nr] = volume;
14553     loop_sound_frame[nr] = FrameCounter;
14554   }
14555
14556   PlaySoundExt(nr, volume, stereo_position, type);
14557 }
14558
14559 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14560 {
14561   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14562                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14563                  y < LEVELY(BY1) ? LEVELY(BY1) :
14564                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14565                  sound_action);
14566 }
14567
14568 static void PlayLevelSoundAction(int x, int y, int action)
14569 {
14570   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14571 }
14572
14573 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14574 {
14575   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14576
14577   if (sound_effect != SND_UNDEFINED)
14578     PlayLevelSound(x, y, sound_effect);
14579 }
14580
14581 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14582                                               int action)
14583 {
14584   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14585
14586   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14587     PlayLevelSound(x, y, sound_effect);
14588 }
14589
14590 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14591 {
14592   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14593
14594   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14595     PlayLevelSound(x, y, sound_effect);
14596 }
14597
14598 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14599 {
14600   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14601
14602   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14603     StopSound(sound_effect);
14604 }
14605
14606 static int getLevelMusicNr()
14607 {
14608   if (levelset.music[level_nr] != MUS_UNDEFINED)
14609     return levelset.music[level_nr];            /* from config file */
14610   else
14611     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14612 }
14613
14614 static void FadeLevelSounds()
14615 {
14616   FadeSounds();
14617 }
14618
14619 static void FadeLevelMusic()
14620 {
14621   int music_nr = getLevelMusicNr();
14622   char *curr_music = getCurrentlyPlayingMusicFilename();
14623   char *next_music = getMusicInfoEntryFilename(music_nr);
14624
14625   if (!strEqual(curr_music, next_music))
14626     FadeMusic();
14627 }
14628
14629 void FadeLevelSoundsAndMusic()
14630 {
14631   FadeLevelSounds();
14632   FadeLevelMusic();
14633 }
14634
14635 static void PlayLevelMusic()
14636 {
14637   int music_nr = getLevelMusicNr();
14638   char *curr_music = getCurrentlyPlayingMusicFilename();
14639   char *next_music = getMusicInfoEntryFilename(music_nr);
14640
14641   if (!strEqual(curr_music, next_music))
14642     PlayMusic(music_nr);
14643 }
14644
14645 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14646 {
14647   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14648   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14649   int x = xx - 1 - offset;
14650   int y = yy - 1 - offset;
14651
14652   switch (sample)
14653   {
14654     case SAMPLE_blank:
14655       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14656       break;
14657
14658     case SAMPLE_roll:
14659       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14660       break;
14661
14662     case SAMPLE_stone:
14663       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14664       break;
14665
14666     case SAMPLE_nut:
14667       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14668       break;
14669
14670     case SAMPLE_crack:
14671       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14672       break;
14673
14674     case SAMPLE_bug:
14675       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14676       break;
14677
14678     case SAMPLE_tank:
14679       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14680       break;
14681
14682     case SAMPLE_android_clone:
14683       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14684       break;
14685
14686     case SAMPLE_android_move:
14687       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14688       break;
14689
14690     case SAMPLE_spring:
14691       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14692       break;
14693
14694     case SAMPLE_slurp:
14695       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14696       break;
14697
14698     case SAMPLE_eater:
14699       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14700       break;
14701
14702     case SAMPLE_eater_eat:
14703       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14704       break;
14705
14706     case SAMPLE_alien:
14707       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14708       break;
14709
14710     case SAMPLE_collect:
14711       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14712       break;
14713
14714     case SAMPLE_diamond:
14715       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14716       break;
14717
14718     case SAMPLE_squash:
14719       /* !!! CHECK THIS !!! */
14720 #if 1
14721       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14722 #else
14723       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14724 #endif
14725       break;
14726
14727     case SAMPLE_wonderfall:
14728       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14729       break;
14730
14731     case SAMPLE_drip:
14732       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14733       break;
14734
14735     case SAMPLE_push:
14736       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14737       break;
14738
14739     case SAMPLE_dirt:
14740       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14741       break;
14742
14743     case SAMPLE_acid:
14744       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14745       break;
14746
14747     case SAMPLE_ball:
14748       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14749       break;
14750
14751     case SAMPLE_grow:
14752       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14753       break;
14754
14755     case SAMPLE_wonder:
14756       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14757       break;
14758
14759     case SAMPLE_door:
14760       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14761       break;
14762
14763     case SAMPLE_exit_open:
14764       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14765       break;
14766
14767     case SAMPLE_exit_leave:
14768       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14769       break;
14770
14771     case SAMPLE_dynamite:
14772       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14773       break;
14774
14775     case SAMPLE_tick:
14776       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14777       break;
14778
14779     case SAMPLE_press:
14780       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14781       break;
14782
14783     case SAMPLE_wheel:
14784       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14785       break;
14786
14787     case SAMPLE_boom:
14788       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14789       break;
14790
14791     case SAMPLE_die:
14792       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14793       break;
14794
14795     case SAMPLE_time:
14796       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14797       break;
14798
14799     default:
14800       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14801       break;
14802   }
14803 }
14804
14805 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14806 {
14807   int element = map_element_SP_to_RND(element_sp);
14808   int action = map_action_SP_to_RND(action_sp);
14809   int offset = (setup.sp_show_border_elements ? 0 : 1);
14810   int x = xx - offset;
14811   int y = yy - offset;
14812
14813   PlayLevelSoundElementAction(x, y, element, action);
14814 }
14815
14816 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14817 {
14818   int element = map_element_MM_to_RND(element_mm);
14819   int action = map_action_MM_to_RND(action_mm);
14820   int offset = 0;
14821   int x = xx - offset;
14822   int y = yy - offset;
14823
14824   if (!IS_MM_ELEMENT(element))
14825     element = EL_MM_DEFAULT;
14826
14827   PlayLevelSoundElementAction(x, y, element, action);
14828 }
14829
14830 void PlaySound_MM(int sound_mm)
14831 {
14832   int sound = map_sound_MM_to_RND(sound_mm);
14833
14834   if (sound == SND_UNDEFINED)
14835     return;
14836
14837   PlaySound(sound);
14838 }
14839
14840 void PlaySoundLoop_MM(int sound_mm)
14841 {
14842   int sound = map_sound_MM_to_RND(sound_mm);
14843
14844   if (sound == SND_UNDEFINED)
14845     return;
14846
14847   PlaySoundLoop(sound);
14848 }
14849
14850 void StopSound_MM(int sound_mm)
14851 {
14852   int sound = map_sound_MM_to_RND(sound_mm);
14853
14854   if (sound == SND_UNDEFINED)
14855     return;
14856
14857   StopSound(sound);
14858 }
14859
14860 void RaiseScore(int value)
14861 {
14862   local_player->score += value;
14863
14864   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14865
14866   DisplayGameControlValues();
14867 }
14868
14869 void RaiseScoreElement(int element)
14870 {
14871   switch (element)
14872   {
14873     case EL_EMERALD:
14874     case EL_BD_DIAMOND:
14875     case EL_EMERALD_YELLOW:
14876     case EL_EMERALD_RED:
14877     case EL_EMERALD_PURPLE:
14878     case EL_SP_INFOTRON:
14879       RaiseScore(level.score[SC_EMERALD]);
14880       break;
14881     case EL_DIAMOND:
14882       RaiseScore(level.score[SC_DIAMOND]);
14883       break;
14884     case EL_CRYSTAL:
14885       RaiseScore(level.score[SC_CRYSTAL]);
14886       break;
14887     case EL_PEARL:
14888       RaiseScore(level.score[SC_PEARL]);
14889       break;
14890     case EL_BUG:
14891     case EL_BD_BUTTERFLY:
14892     case EL_SP_ELECTRON:
14893       RaiseScore(level.score[SC_BUG]);
14894       break;
14895     case EL_SPACESHIP:
14896     case EL_BD_FIREFLY:
14897     case EL_SP_SNIKSNAK:
14898       RaiseScore(level.score[SC_SPACESHIP]);
14899       break;
14900     case EL_YAMYAM:
14901     case EL_DARK_YAMYAM:
14902       RaiseScore(level.score[SC_YAMYAM]);
14903       break;
14904     case EL_ROBOT:
14905       RaiseScore(level.score[SC_ROBOT]);
14906       break;
14907     case EL_PACMAN:
14908       RaiseScore(level.score[SC_PACMAN]);
14909       break;
14910     case EL_NUT:
14911       RaiseScore(level.score[SC_NUT]);
14912       break;
14913     case EL_DYNAMITE:
14914     case EL_EM_DYNAMITE:
14915     case EL_SP_DISK_RED:
14916     case EL_DYNABOMB_INCREASE_NUMBER:
14917     case EL_DYNABOMB_INCREASE_SIZE:
14918     case EL_DYNABOMB_INCREASE_POWER:
14919       RaiseScore(level.score[SC_DYNAMITE]);
14920       break;
14921     case EL_SHIELD_NORMAL:
14922     case EL_SHIELD_DEADLY:
14923       RaiseScore(level.score[SC_SHIELD]);
14924       break;
14925     case EL_EXTRA_TIME:
14926       RaiseScore(level.extra_time_score);
14927       break;
14928     case EL_KEY_1:
14929     case EL_KEY_2:
14930     case EL_KEY_3:
14931     case EL_KEY_4:
14932     case EL_EM_KEY_1:
14933     case EL_EM_KEY_2:
14934     case EL_EM_KEY_3:
14935     case EL_EM_KEY_4:
14936     case EL_EMC_KEY_5:
14937     case EL_EMC_KEY_6:
14938     case EL_EMC_KEY_7:
14939     case EL_EMC_KEY_8:
14940     case EL_DC_KEY_WHITE:
14941       RaiseScore(level.score[SC_KEY]);
14942       break;
14943     default:
14944       RaiseScore(element_info[element].collect_score);
14945       break;
14946   }
14947 }
14948
14949 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14950 {
14951   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14952   {
14953     /* closing door required in case of envelope style request dialogs */
14954     if (!skip_request)
14955       CloseDoor(DOOR_CLOSE_1);
14956
14957     if (network.enabled)
14958       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14959     else
14960     {
14961       if (quick_quit)
14962         FadeSkipNextFadeIn();
14963
14964       SetGameStatus(GAME_MODE_MAIN);
14965
14966       DrawMainMenu();
14967     }
14968   }
14969   else          /* continue playing the game */
14970   {
14971     if (tape.playing && tape.deactivate_display)
14972       TapeDeactivateDisplayOff(TRUE);
14973
14974     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14975
14976     if (tape.playing && tape.deactivate_display)
14977       TapeDeactivateDisplayOn();
14978   }
14979 }
14980
14981 void RequestQuitGame(boolean ask_if_really_quit)
14982 {
14983   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14984   boolean skip_request = AllPlayersGone || quick_quit;
14985
14986   RequestQuitGameExt(skip_request, quick_quit,
14987                      "Do you really want to quit the game?");
14988 }
14989
14990 void RequestRestartGame(char *message)
14991 {
14992   game.restart_game_message = NULL;
14993
14994   if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14995   {
14996     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14997   }
14998   else
14999   {
15000     SetGameStatus(GAME_MODE_MAIN);
15001
15002     DrawMainMenu();
15003   }
15004 }
15005
15006
15007 /* ------------------------------------------------------------------------- */
15008 /* random generator functions                                                */
15009 /* ------------------------------------------------------------------------- */
15010
15011 unsigned int InitEngineRandom_RND(int seed)
15012 {
15013   game.num_random_calls = 0;
15014
15015   return InitEngineRandom(seed);
15016 }
15017
15018 unsigned int RND(int max)
15019 {
15020   if (max > 0)
15021   {
15022     game.num_random_calls++;
15023
15024     return GetEngineRandom(max);
15025   }
15026
15027   return 0;
15028 }
15029
15030
15031 /* ------------------------------------------------------------------------- */
15032 /* game engine snapshot handling functions                                   */
15033 /* ------------------------------------------------------------------------- */
15034
15035 struct EngineSnapshotInfo
15036 {
15037   /* runtime values for custom element collect score */
15038   int collect_score[NUM_CUSTOM_ELEMENTS];
15039
15040   /* runtime values for group element choice position */
15041   int choice_pos[NUM_GROUP_ELEMENTS];
15042
15043   /* runtime values for belt position animations */
15044   int belt_graphic[4][NUM_BELT_PARTS];
15045   int belt_anim_mode[4][NUM_BELT_PARTS];
15046 };
15047
15048 static struct EngineSnapshotInfo engine_snapshot_rnd;
15049 static char *snapshot_level_identifier = NULL;
15050 static int snapshot_level_nr = -1;
15051
15052 static void SaveEngineSnapshotValues_RND()
15053 {
15054   static int belt_base_active_element[4] =
15055   {
15056     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15057     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15058     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15059     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15060   };
15061   int i, j;
15062
15063   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15064   {
15065     int element = EL_CUSTOM_START + i;
15066
15067     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15068   }
15069
15070   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15071   {
15072     int element = EL_GROUP_START + i;
15073
15074     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15075   }
15076
15077   for (i = 0; i < 4; i++)
15078   {
15079     for (j = 0; j < NUM_BELT_PARTS; j++)
15080     {
15081       int element = belt_base_active_element[i] + j;
15082       int graphic = el2img(element);
15083       int anim_mode = graphic_info[graphic].anim_mode;
15084
15085       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15086       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15087     }
15088   }
15089 }
15090
15091 static void LoadEngineSnapshotValues_RND()
15092 {
15093   unsigned int num_random_calls = game.num_random_calls;
15094   int i, j;
15095
15096   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15097   {
15098     int element = EL_CUSTOM_START + i;
15099
15100     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15101   }
15102
15103   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15104   {
15105     int element = EL_GROUP_START + i;
15106
15107     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15108   }
15109
15110   for (i = 0; i < 4; i++)
15111   {
15112     for (j = 0; j < NUM_BELT_PARTS; j++)
15113     {
15114       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15115       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15116
15117       graphic_info[graphic].anim_mode = anim_mode;
15118     }
15119   }
15120
15121   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15122   {
15123     InitRND(tape.random_seed);
15124     for (i = 0; i < num_random_calls; i++)
15125       RND(1);
15126   }
15127
15128   if (game.num_random_calls != num_random_calls)
15129   {
15130     Error(ERR_INFO, "number of random calls out of sync");
15131     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15132     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15133     Error(ERR_EXIT, "this should not happen -- please debug");
15134   }
15135 }
15136
15137 void FreeEngineSnapshotSingle()
15138 {
15139   FreeSnapshotSingle();
15140
15141   setString(&snapshot_level_identifier, NULL);
15142   snapshot_level_nr = -1;
15143 }
15144
15145 void FreeEngineSnapshotList()
15146 {
15147   FreeSnapshotList();
15148 }
15149
15150 ListNode *SaveEngineSnapshotBuffers()
15151 {
15152   ListNode *buffers = NULL;
15153
15154   /* copy some special values to a structure better suited for the snapshot */
15155
15156   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15157     SaveEngineSnapshotValues_RND();
15158   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15159     SaveEngineSnapshotValues_EM();
15160   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15161     SaveEngineSnapshotValues_SP(&buffers);
15162   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15163     SaveEngineSnapshotValues_MM(&buffers);
15164
15165   /* save values stored in special snapshot structure */
15166
15167   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15168     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15169   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15170     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15171   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15172     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15173   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15174     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15175
15176   /* save further RND engine values */
15177
15178   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15179   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15180   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15181
15182   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15183   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15184   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15185   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15186
15187   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15188   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15189   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15190   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15191   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15192
15193   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15194   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15195   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15196
15197   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15198
15199   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15200
15201   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15202   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15203
15204   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15205   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15206   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15207   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15208   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15209   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15210   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15211   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15212   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15213   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15214   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15215   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15216   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15217   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15218   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15219   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15220   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15221   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15222
15223   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15224   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15225
15226   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15227   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15228   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15229
15230   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15231   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15232
15233   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15234   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15235   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15236   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15237   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15238
15239   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15240   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15241
15242 #if 0
15243   ListNode *node = engine_snapshot_list_rnd;
15244   int num_bytes = 0;
15245
15246   while (node != NULL)
15247   {
15248     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15249
15250     node = node->next;
15251   }
15252
15253   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15254 #endif
15255
15256   return buffers;
15257 }
15258
15259 void SaveEngineSnapshotSingle()
15260 {
15261   ListNode *buffers = SaveEngineSnapshotBuffers();
15262
15263   /* finally save all snapshot buffers to single snapshot */
15264   SaveSnapshotSingle(buffers);
15265
15266   /* save level identification information */
15267   setString(&snapshot_level_identifier, leveldir_current->identifier);
15268   snapshot_level_nr = level_nr;
15269 }
15270
15271 boolean CheckSaveEngineSnapshotToList()
15272 {
15273   boolean save_snapshot =
15274     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15275      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15276       game.snapshot.changed_action) ||
15277      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15278       game.snapshot.collected_item));
15279
15280   game.snapshot.changed_action = FALSE;
15281   game.snapshot.collected_item = FALSE;
15282   game.snapshot.save_snapshot = save_snapshot;
15283
15284   return save_snapshot;
15285 }
15286
15287 void SaveEngineSnapshotToList()
15288 {
15289   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15290       tape.quick_resume)
15291     return;
15292
15293   ListNode *buffers = SaveEngineSnapshotBuffers();
15294
15295   /* finally save all snapshot buffers to snapshot list */
15296   SaveSnapshotToList(buffers);
15297 }
15298
15299 void SaveEngineSnapshotToListInitial()
15300 {
15301   FreeEngineSnapshotList();
15302
15303   SaveEngineSnapshotToList();
15304 }
15305
15306 void LoadEngineSnapshotValues()
15307 {
15308   /* restore special values from snapshot structure */
15309
15310   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15311     LoadEngineSnapshotValues_RND();
15312   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15313     LoadEngineSnapshotValues_EM();
15314   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15315     LoadEngineSnapshotValues_SP();
15316   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15317     LoadEngineSnapshotValues_MM();
15318 }
15319
15320 void LoadEngineSnapshotSingle()
15321 {
15322   LoadSnapshotSingle();
15323
15324   LoadEngineSnapshotValues();
15325 }
15326
15327 void LoadEngineSnapshot_Undo(int steps)
15328 {
15329   LoadSnapshotFromList_Older(steps);
15330
15331   LoadEngineSnapshotValues();
15332 }
15333
15334 void LoadEngineSnapshot_Redo(int steps)
15335 {
15336   LoadSnapshotFromList_Newer(steps);
15337
15338   LoadEngineSnapshotValues();
15339 }
15340
15341 boolean CheckEngineSnapshotSingle()
15342 {
15343   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15344           snapshot_level_nr == level_nr);
15345 }
15346
15347 boolean CheckEngineSnapshotList()
15348 {
15349   return CheckSnapshotList();
15350 }
15351
15352
15353 /* ---------- new game button stuff ---------------------------------------- */
15354
15355 static struct
15356 {
15357   int graphic;
15358   struct XY *pos;
15359   int gadget_id;
15360   boolean *setup_value;
15361   boolean allowed_on_tape;
15362   char *infotext;
15363 } gamebutton_info[NUM_GAME_BUTTONS] =
15364 {
15365   {
15366     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15367     GAME_CTRL_ID_STOP,                          NULL,
15368     TRUE,                                       "stop game"
15369   },
15370   {
15371     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15372     GAME_CTRL_ID_PAUSE,                         NULL,
15373     TRUE,                                       "pause game"
15374   },
15375   {
15376     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15377     GAME_CTRL_ID_PLAY,                          NULL,
15378     TRUE,                                       "play game"
15379   },
15380   {
15381     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15382     GAME_CTRL_ID_UNDO,                          NULL,
15383     TRUE,                                       "undo step"
15384   },
15385   {
15386     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15387     GAME_CTRL_ID_REDO,                          NULL,
15388     TRUE,                                       "redo step"
15389   },
15390   {
15391     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15392     GAME_CTRL_ID_SAVE,                          NULL,
15393     TRUE,                                       "save game"
15394   },
15395   {
15396     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15397     GAME_CTRL_ID_PAUSE2,                        NULL,
15398     TRUE,                                       "pause game"
15399   },
15400   {
15401     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15402     GAME_CTRL_ID_LOAD,                          NULL,
15403     TRUE,                                       "load game"
15404   },
15405   {
15406     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15407     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15408     FALSE,                                      "stop game"
15409   },
15410   {
15411     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15412     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15413     FALSE,                                      "pause game"
15414   },
15415   {
15416     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15417     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15418     FALSE,                                      "play game"
15419   },
15420   {
15421     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15422     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15423     TRUE,                                       "background music on/off"
15424   },
15425   {
15426     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15427     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15428     TRUE,                                       "sound loops on/off"
15429   },
15430   {
15431     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15432     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15433     TRUE,                                       "normal sounds on/off"
15434   },
15435   {
15436     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15437     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15438     FALSE,                                      "background music on/off"
15439   },
15440   {
15441     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15442     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15443     FALSE,                                      "sound loops on/off"
15444   },
15445   {
15446     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15447     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15448     FALSE,                                      "normal sounds on/off"
15449   }
15450 };
15451
15452 void CreateGameButtons()
15453 {
15454   int i;
15455
15456   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15457   {
15458     int graphic = gamebutton_info[i].graphic;
15459     struct GraphicInfo *gfx = &graphic_info[graphic];
15460     struct XY *pos = gamebutton_info[i].pos;
15461     struct GadgetInfo *gi;
15462     int button_type;
15463     boolean checked;
15464     unsigned int event_mask;
15465     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15466     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15467     int base_x = (on_tape ? VX : DX);
15468     int base_y = (on_tape ? VY : DY);
15469     int gd_x   = gfx->src_x;
15470     int gd_y   = gfx->src_y;
15471     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15472     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15473     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15474     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15475     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15476     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15477     int id = i;
15478
15479     if (gfx->bitmap == NULL)
15480     {
15481       game_gadget[id] = NULL;
15482
15483       continue;
15484     }
15485
15486     if (id == GAME_CTRL_ID_STOP ||
15487         id == GAME_CTRL_ID_PANEL_STOP ||
15488         id == GAME_CTRL_ID_PLAY ||
15489         id == GAME_CTRL_ID_PANEL_PLAY ||
15490         id == GAME_CTRL_ID_SAVE ||
15491         id == GAME_CTRL_ID_LOAD)
15492     {
15493       button_type = GD_TYPE_NORMAL_BUTTON;
15494       checked = FALSE;
15495       event_mask = GD_EVENT_RELEASED;
15496     }
15497     else if (id == GAME_CTRL_ID_UNDO ||
15498              id == GAME_CTRL_ID_REDO)
15499     {
15500       button_type = GD_TYPE_NORMAL_BUTTON;
15501       checked = FALSE;
15502       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15503     }
15504     else
15505     {
15506       button_type = GD_TYPE_CHECK_BUTTON;
15507       checked = (gamebutton_info[i].setup_value != NULL ?
15508                  *gamebutton_info[i].setup_value : FALSE);
15509       event_mask = GD_EVENT_PRESSED;
15510     }
15511
15512     gi = CreateGadget(GDI_CUSTOM_ID, id,
15513                       GDI_IMAGE_ID, graphic,
15514                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15515                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15516                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15517                       GDI_WIDTH, gfx->width,
15518                       GDI_HEIGHT, gfx->height,
15519                       GDI_TYPE, button_type,
15520                       GDI_STATE, GD_BUTTON_UNPRESSED,
15521                       GDI_CHECKED, checked,
15522                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15523                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15524                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15525                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15526                       GDI_DIRECT_DRAW, FALSE,
15527                       GDI_EVENT_MASK, event_mask,
15528                       GDI_CALLBACK_ACTION, HandleGameButtons,
15529                       GDI_END);
15530
15531     if (gi == NULL)
15532       Error(ERR_EXIT, "cannot create gadget");
15533
15534     game_gadget[id] = gi;
15535   }
15536 }
15537
15538 void FreeGameButtons()
15539 {
15540   int i;
15541
15542   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15543     FreeGadget(game_gadget[i]);
15544 }
15545
15546 static void UnmapGameButtonsAtSamePosition(int id)
15547 {
15548   int i;
15549
15550   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15551     if (i != id &&
15552         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15553         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15554       UnmapGadget(game_gadget[i]);
15555 }
15556
15557 static void UnmapGameButtonsAtSamePosition_All()
15558 {
15559   if (setup.show_snapshot_buttons)
15560   {
15561     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15562     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15563     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15564   }
15565   else
15566   {
15567     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15568     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15569     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15570
15571     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15572     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15573     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15574   }
15575 }
15576
15577 static void MapGameButtonsAtSamePosition(int id)
15578 {
15579   int i;
15580
15581   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15582     if (i != id &&
15583         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15584         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15585       MapGadget(game_gadget[i]);
15586
15587   UnmapGameButtonsAtSamePosition_All();
15588 }
15589
15590 void MapUndoRedoButtons()
15591 {
15592   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15593   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15594
15595   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15596   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15597
15598   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15599 }
15600
15601 void UnmapUndoRedoButtons()
15602 {
15603   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15604   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15605
15606   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15607   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15608
15609   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15610 }
15611
15612 void MapGameButtonsExt(boolean on_tape)
15613 {
15614   int i;
15615
15616   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15617     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15618         i != GAME_CTRL_ID_UNDO &&
15619         i != GAME_CTRL_ID_REDO)
15620       MapGadget(game_gadget[i]);
15621
15622   UnmapGameButtonsAtSamePosition_All();
15623
15624   RedrawGameButtons();
15625 }
15626
15627 void UnmapGameButtonsExt(boolean on_tape)
15628 {
15629   int i;
15630
15631   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15632     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15633       UnmapGadget(game_gadget[i]);
15634 }
15635
15636 void RedrawGameButtonsExt(boolean on_tape)
15637 {
15638   int i;
15639
15640   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15641     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15642       RedrawGadget(game_gadget[i]);
15643
15644   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15645   redraw_mask &= ~REDRAW_ALL;
15646 }
15647
15648 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15649 {
15650   if (gi == NULL)
15651     return;
15652
15653   gi->checked = state;
15654 }
15655
15656 void RedrawSoundButtonGadget(int id)
15657 {
15658   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15659              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15660              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15661              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15662              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15663              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15664              id);
15665
15666   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15667   RedrawGadget(game_gadget[id2]);
15668 }
15669
15670 void MapGameButtons()
15671 {
15672   MapGameButtonsExt(FALSE);
15673 }
15674
15675 void UnmapGameButtons()
15676 {
15677   UnmapGameButtonsExt(FALSE);
15678 }
15679
15680 void RedrawGameButtons()
15681 {
15682   RedrawGameButtonsExt(FALSE);
15683 }
15684
15685 void MapGameButtonsOnTape()
15686 {
15687   MapGameButtonsExt(TRUE);
15688 }
15689
15690 void UnmapGameButtonsOnTape()
15691 {
15692   UnmapGameButtonsExt(TRUE);
15693 }
15694
15695 void RedrawGameButtonsOnTape()
15696 {
15697   RedrawGameButtonsExt(TRUE);
15698 }
15699
15700 void GameUndoRedoExt()
15701 {
15702   ClearPlayerAction();
15703
15704   tape.pausing = TRUE;
15705
15706   RedrawPlayfield();
15707   UpdateAndDisplayGameControlValues();
15708
15709   DrawCompleteVideoDisplay();
15710   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15711   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15712   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15713
15714   BackToFront();
15715 }
15716
15717 void GameUndo(int steps)
15718 {
15719   if (!CheckEngineSnapshotList())
15720     return;
15721
15722   LoadEngineSnapshot_Undo(steps);
15723
15724   GameUndoRedoExt();
15725 }
15726
15727 void GameRedo(int steps)
15728 {
15729   if (!CheckEngineSnapshotList())
15730     return;
15731
15732   LoadEngineSnapshot_Redo(steps);
15733
15734   GameUndoRedoExt();
15735 }
15736
15737 static void HandleGameButtonsExt(int id, int button)
15738 {
15739   static boolean game_undo_executed = FALSE;
15740   int steps = BUTTON_STEPSIZE(button);
15741   boolean handle_game_buttons =
15742     (game_status == GAME_MODE_PLAYING ||
15743      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15744
15745   if (!handle_game_buttons)
15746     return;
15747
15748   switch (id)
15749   {
15750     case GAME_CTRL_ID_STOP:
15751     case GAME_CTRL_ID_PANEL_STOP:
15752       if (game_status == GAME_MODE_MAIN)
15753         break;
15754
15755       if (tape.playing)
15756         TapeStop();
15757       else
15758         RequestQuitGame(TRUE);
15759
15760       break;
15761
15762     case GAME_CTRL_ID_PAUSE:
15763     case GAME_CTRL_ID_PAUSE2:
15764     case GAME_CTRL_ID_PANEL_PAUSE:
15765       if (network.enabled && game_status == GAME_MODE_PLAYING)
15766       {
15767         if (tape.pausing)
15768           SendToServer_ContinuePlaying();
15769         else
15770           SendToServer_PausePlaying();
15771       }
15772       else
15773         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15774
15775       game_undo_executed = FALSE;
15776
15777       break;
15778
15779     case GAME_CTRL_ID_PLAY:
15780     case GAME_CTRL_ID_PANEL_PLAY:
15781       if (game_status == GAME_MODE_MAIN)
15782       {
15783         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15784       }
15785       else if (tape.pausing)
15786       {
15787         if (network.enabled)
15788           SendToServer_ContinuePlaying();
15789         else
15790           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15791       }
15792       break;
15793
15794     case GAME_CTRL_ID_UNDO:
15795       // Important: When using "save snapshot when collecting an item" mode,
15796       // load last (current) snapshot for first "undo" after pressing "pause"
15797       // (else the last-but-one snapshot would be loaded, because the snapshot
15798       // pointer already points to the last snapshot when pressing "pause",
15799       // which is fine for "every step/move" mode, but not for "every collect")
15800       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15801           !game_undo_executed)
15802         steps--;
15803
15804       game_undo_executed = TRUE;
15805
15806       GameUndo(steps);
15807       break;
15808
15809     case GAME_CTRL_ID_REDO:
15810       GameRedo(steps);
15811       break;
15812
15813     case GAME_CTRL_ID_SAVE:
15814       TapeQuickSave();
15815       break;
15816
15817     case GAME_CTRL_ID_LOAD:
15818       TapeQuickLoad();
15819       break;
15820
15821     case SOUND_CTRL_ID_MUSIC:
15822     case SOUND_CTRL_ID_PANEL_MUSIC:
15823       if (setup.sound_music)
15824       { 
15825         setup.sound_music = FALSE;
15826
15827         FadeMusic();
15828       }
15829       else if (audio.music_available)
15830       { 
15831         setup.sound = setup.sound_music = TRUE;
15832
15833         SetAudioMode(setup.sound);
15834
15835         if (game_status == GAME_MODE_PLAYING)
15836           PlayLevelMusic();
15837       }
15838
15839       RedrawSoundButtonGadget(id);
15840
15841       break;
15842
15843     case SOUND_CTRL_ID_LOOPS:
15844     case SOUND_CTRL_ID_PANEL_LOOPS:
15845       if (setup.sound_loops)
15846         setup.sound_loops = FALSE;
15847       else if (audio.loops_available)
15848       {
15849         setup.sound = setup.sound_loops = TRUE;
15850
15851         SetAudioMode(setup.sound);
15852       }
15853
15854       RedrawSoundButtonGadget(id);
15855
15856       break;
15857
15858     case SOUND_CTRL_ID_SIMPLE:
15859     case SOUND_CTRL_ID_PANEL_SIMPLE:
15860       if (setup.sound_simple)
15861         setup.sound_simple = FALSE;
15862       else if (audio.sound_available)
15863       {
15864         setup.sound = setup.sound_simple = TRUE;
15865
15866         SetAudioMode(setup.sound);
15867       }
15868
15869       RedrawSoundButtonGadget(id);
15870
15871       break;
15872
15873     default:
15874       break;
15875   }
15876 }
15877
15878 static void HandleGameButtons(struct GadgetInfo *gi)
15879 {
15880   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15881 }
15882
15883 void HandleSoundButtonKeys(Key key)
15884 {
15885   if (key == setup.shortcut.sound_simple)
15886     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15887   else if (key == setup.shortcut.sound_loops)
15888     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15889   else if (key == setup.shortcut.sound_music)
15890     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15891 }