rnd-20080120-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61
62 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
63
64 #define USE_DELAYED_GFX_REDRAW          (USE_NEW_STUFF          * 1)
65
66 #if USE_DELAYED_GFX_REDRAW
67 #define TEST_DrawLevelField(x, y)                               \
68         GfxRedraw[x][y] |= GFX_REDRAW_TILE
69 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
70         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
71 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
72         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
73 #define TEST_DrawTwinkleOnField(x, y)                           \
74         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
75 #else
76 #define TEST_DrawLevelField(x, y)                               \
77              DrawLevelField(x, y)
78 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
79              DrawLevelFieldCrumbledSand(x, y)
80 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
81              DrawLevelFieldCrumbledSandNeighbours(x, y)
82 #define TEST_DrawTwinkleOnField(x, y)                           \
83              DrawTwinkleOnField(x, y)
84 #endif
85
86
87 /* for DigField() */
88 #define DF_NO_PUSH              0
89 #define DF_DIG                  1
90 #define DF_SNAP                 2
91
92 /* for MovePlayer() */
93 #define MP_NO_ACTION            0
94 #define MP_MOVING               1
95 #define MP_ACTION               2
96 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
97
98 /* for ScrollPlayer() */
99 #define SCROLL_INIT             0
100 #define SCROLL_GO_ON            1
101
102 /* for Bang()/Explode() */
103 #define EX_PHASE_START          0
104 #define EX_TYPE_NONE            0
105 #define EX_TYPE_NORMAL          (1 << 0)
106 #define EX_TYPE_CENTER          (1 << 1)
107 #define EX_TYPE_BORDER          (1 << 2)
108 #define EX_TYPE_CROSS           (1 << 3)
109 #define EX_TYPE_DYNA            (1 << 4)
110 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
111
112 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
113 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
114 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
115 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
116
117 /* special positions in the game control window (relative to control window) */
118 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
119 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
120 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
121 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
122 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
123 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
124 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
125 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
126 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
127 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
128 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
129 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
130 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
131 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
132 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
133 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
134
135 /* special positions in the game control window (relative to main window) */
136 #define DX_LEVEL1               (DX + XX_LEVEL1)
137 #define DX_LEVEL2               (DX + XX_LEVEL2)
138 #define DX_LEVEL                (DX + XX_LEVEL)
139 #define DY_LEVEL                (DY + YY_LEVEL)
140 #define DX_EMERALDS             (DX + XX_EMERALDS)
141 #define DY_EMERALDS             (DY + YY_EMERALDS)
142 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
143 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
144 #define DX_KEYS                 (DX + XX_KEYS)
145 #define DY_KEYS                 (DY + YY_KEYS)
146 #define DX_SCORE                (DX + XX_SCORE)
147 #define DY_SCORE                (DY + YY_SCORE)
148 #define DX_TIME1                (DX + XX_TIME1)
149 #define DX_TIME2                (DX + XX_TIME2)
150 #define DX_TIME                 (DX + XX_TIME)
151 #define DY_TIME                 (DY + YY_TIME)
152
153 #if 1
154 /* game panel display and control definitions */
155
156 #define GAME_PANEL_LEVEL_NUMBER                 0
157 #define GAME_PANEL_GEMS                         1
158 #define GAME_PANEL_INVENTORY_COUNT              2
159 #define GAME_PANEL_INVENTORY_FIRST_1            3
160 #define GAME_PANEL_INVENTORY_FIRST_2            4
161 #define GAME_PANEL_INVENTORY_FIRST_3            5
162 #define GAME_PANEL_INVENTORY_FIRST_4            6
163 #define GAME_PANEL_INVENTORY_FIRST_5            7
164 #define GAME_PANEL_INVENTORY_FIRST_6            8
165 #define GAME_PANEL_INVENTORY_FIRST_7            9
166 #define GAME_PANEL_INVENTORY_FIRST_8            10
167 #define GAME_PANEL_INVENTORY_LAST_1             11
168 #define GAME_PANEL_INVENTORY_LAST_2             12
169 #define GAME_PANEL_INVENTORY_LAST_3             13
170 #define GAME_PANEL_INVENTORY_LAST_4             14
171 #define GAME_PANEL_INVENTORY_LAST_5             15
172 #define GAME_PANEL_INVENTORY_LAST_6             16
173 #define GAME_PANEL_INVENTORY_LAST_7             17
174 #define GAME_PANEL_INVENTORY_LAST_8             18
175 #define GAME_PANEL_KEY_1                        19
176 #define GAME_PANEL_KEY_2                        20
177 #define GAME_PANEL_KEY_3                        21
178 #define GAME_PANEL_KEY_4                        22
179 #define GAME_PANEL_KEY_5                        23
180 #define GAME_PANEL_KEY_6                        24
181 #define GAME_PANEL_KEY_7                        25
182 #define GAME_PANEL_KEY_8                        26
183 #define GAME_PANEL_KEY_WHITE                    27
184 #define GAME_PANEL_KEY_WHITE_COUNT              28
185 #define GAME_PANEL_SCORE                        29
186 #define GAME_PANEL_HIGHSCORE                    30
187 #define GAME_PANEL_TIME                         31
188 #define GAME_PANEL_TIME_HH                      32
189 #define GAME_PANEL_TIME_MM                      33
190 #define GAME_PANEL_TIME_SS                      34
191 #define GAME_PANEL_SHIELD_NORMAL                35
192 #define GAME_PANEL_SHIELD_NORMAL_TIME           36
193 #define GAME_PANEL_SHIELD_DEADLY                37
194 #define GAME_PANEL_SHIELD_DEADLY_TIME           38
195 #define GAME_PANEL_EXIT                         39
196 #define GAME_PANEL_EMC_MAGIC_BALL               40
197 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        41
198 #define GAME_PANEL_LIGHT_SWITCH                 42
199 #define GAME_PANEL_LIGHT_SWITCH_TIME            43
200 #define GAME_PANEL_TIMEGATE_SWITCH              44
201 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         45
202 #define GAME_PANEL_SWITCHGATE_SWITCH            46
203 #define GAME_PANEL_EMC_LENSES                   47
204 #define GAME_PANEL_EMC_LENSES_TIME              48
205 #define GAME_PANEL_EMC_MAGNIFIER                49
206 #define GAME_PANEL_EMC_MAGNIFIER_TIME           50
207 #define GAME_PANEL_BALLOON_SWITCH               51
208 #define GAME_PANEL_DYNABOMB_NUMBER              52
209 #define GAME_PANEL_DYNABOMB_SIZE                53
210 #define GAME_PANEL_DYNABOMB_POWER               54
211 #define GAME_PANEL_PENGUINS                     55
212 #define GAME_PANEL_SOKOBAN_OBJECTS              56
213 #define GAME_PANEL_SOKOBAN_FIELDS               57
214 #define GAME_PANEL_ROBOT_WHEEL                  58
215 #define GAME_PANEL_CONVEYOR_BELT_1              59
216 #define GAME_PANEL_CONVEYOR_BELT_2              60
217 #define GAME_PANEL_CONVEYOR_BELT_3              61
218 #define GAME_PANEL_CONVEYOR_BELT_4              62
219 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       63
220 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       64
221 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       65
222 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       66
223 #define GAME_PANEL_MAGIC_WALL                   67
224 #define GAME_PANEL_MAGIC_WALL_TIME              68
225 #define GAME_PANEL_GRAVITY_STATE                69
226 #define GAME_PANEL_GRAPHIC_1                    70
227 #define GAME_PANEL_GRAPHIC_2                    71
228 #define GAME_PANEL_GRAPHIC_3                    72
229 #define GAME_PANEL_GRAPHIC_4                    73
230 #define GAME_PANEL_GRAPHIC_5                    74
231 #define GAME_PANEL_GRAPHIC_6                    75
232 #define GAME_PANEL_GRAPHIC_7                    76
233 #define GAME_PANEL_GRAPHIC_8                    77
234 #define GAME_PANEL_ELEMENT_1                    78
235 #define GAME_PANEL_ELEMENT_2                    79
236 #define GAME_PANEL_ELEMENT_3                    80
237 #define GAME_PANEL_ELEMENT_4                    81
238 #define GAME_PANEL_ELEMENT_5                    82
239 #define GAME_PANEL_ELEMENT_6                    83
240 #define GAME_PANEL_ELEMENT_7                    84
241 #define GAME_PANEL_ELEMENT_8                    85
242 #define GAME_PANEL_ELEMENT_COUNT_1              86
243 #define GAME_PANEL_ELEMENT_COUNT_2              87
244 #define GAME_PANEL_ELEMENT_COUNT_3              88
245 #define GAME_PANEL_ELEMENT_COUNT_4              89
246 #define GAME_PANEL_ELEMENT_COUNT_5              90
247 #define GAME_PANEL_ELEMENT_COUNT_6              91
248 #define GAME_PANEL_ELEMENT_COUNT_7              92
249 #define GAME_PANEL_ELEMENT_COUNT_8              93
250 #define GAME_PANEL_CE_SCORE_1                   94
251 #define GAME_PANEL_CE_SCORE_2                   95
252 #define GAME_PANEL_CE_SCORE_3                   96
253 #define GAME_PANEL_CE_SCORE_4                   97
254 #define GAME_PANEL_CE_SCORE_5                   98
255 #define GAME_PANEL_CE_SCORE_6                   99
256 #define GAME_PANEL_CE_SCORE_7                   100
257 #define GAME_PANEL_CE_SCORE_8                   101
258 #define GAME_PANEL_CE_SCORE_1_ELEMENT           102
259 #define GAME_PANEL_CE_SCORE_2_ELEMENT           103
260 #define GAME_PANEL_CE_SCORE_3_ELEMENT           104
261 #define GAME_PANEL_CE_SCORE_4_ELEMENT           105
262 #define GAME_PANEL_CE_SCORE_5_ELEMENT           106
263 #define GAME_PANEL_CE_SCORE_6_ELEMENT           107
264 #define GAME_PANEL_CE_SCORE_7_ELEMENT           108
265 #define GAME_PANEL_CE_SCORE_8_ELEMENT           109
266 #define GAME_PANEL_PLAYER_NAME                  110
267 #define GAME_PANEL_LEVEL_NAME                   111
268 #define GAME_PANEL_LEVEL_AUTHOR                 112
269
270 #define NUM_GAME_PANEL_CONTROLS                 113
271
272 struct GamePanelOrderInfo
273 {
274   int nr;
275   int sort_priority;
276 };
277
278 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
279
280 struct GamePanelControlInfo
281 {
282   int nr;
283
284   struct TextPosInfo *pos;
285   int type;
286
287   int value, last_value;
288   int frame, last_frame;
289   int gfx_frame;
290   int gfx_random;
291 };
292
293 static struct GamePanelControlInfo game_panel_controls[] =
294 {
295   {
296     GAME_PANEL_LEVEL_NUMBER,
297     &game.panel.level_number,
298     TYPE_INTEGER,
299   },
300   {
301     GAME_PANEL_GEMS,
302     &game.panel.gems,
303     TYPE_INTEGER,
304   },
305   {
306     GAME_PANEL_INVENTORY_COUNT,
307     &game.panel.inventory_count,
308     TYPE_INTEGER,
309   },
310   {
311     GAME_PANEL_INVENTORY_FIRST_1,
312     &game.panel.inventory_first[0],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_FIRST_2,
317     &game.panel.inventory_first[1],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_FIRST_3,
322     &game.panel.inventory_first[2],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_FIRST_4,
327     &game.panel.inventory_first[3],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_INVENTORY_FIRST_5,
332     &game.panel.inventory_first[4],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_INVENTORY_FIRST_6,
337     &game.panel.inventory_first[5],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_INVENTORY_FIRST_7,
342     &game.panel.inventory_first[6],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_INVENTORY_FIRST_8,
347     &game.panel.inventory_first[7],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_INVENTORY_LAST_1,
352     &game.panel.inventory_last[0],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_INVENTORY_LAST_2,
357     &game.panel.inventory_last[1],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_INVENTORY_LAST_3,
362     &game.panel.inventory_last[2],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_INVENTORY_LAST_4,
367     &game.panel.inventory_last[3],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_INVENTORY_LAST_5,
372     &game.panel.inventory_last[4],
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_INVENTORY_LAST_6,
377     &game.panel.inventory_last[5],
378     TYPE_ELEMENT,
379   },
380   {
381     GAME_PANEL_INVENTORY_LAST_7,
382     &game.panel.inventory_last[6],
383     TYPE_ELEMENT,
384   },
385   {
386     GAME_PANEL_INVENTORY_LAST_8,
387     &game.panel.inventory_last[7],
388     TYPE_ELEMENT,
389   },
390   {
391     GAME_PANEL_KEY_1,
392     &game.panel.key[0],
393     TYPE_ELEMENT,
394   },
395   {
396     GAME_PANEL_KEY_2,
397     &game.panel.key[1],
398     TYPE_ELEMENT,
399   },
400   {
401     GAME_PANEL_KEY_3,
402     &game.panel.key[2],
403     TYPE_ELEMENT,
404   },
405   {
406     GAME_PANEL_KEY_4,
407     &game.panel.key[3],
408     TYPE_ELEMENT,
409   },
410   {
411     GAME_PANEL_KEY_5,
412     &game.panel.key[4],
413     TYPE_ELEMENT,
414   },
415   {
416     GAME_PANEL_KEY_6,
417     &game.panel.key[5],
418     TYPE_ELEMENT,
419   },
420   {
421     GAME_PANEL_KEY_7,
422     &game.panel.key[6],
423     TYPE_ELEMENT,
424   },
425   {
426     GAME_PANEL_KEY_8,
427     &game.panel.key[7],
428     TYPE_ELEMENT,
429   },
430   {
431     GAME_PANEL_KEY_WHITE,
432     &game.panel.key_white,
433     TYPE_ELEMENT,
434   },
435   {
436     GAME_PANEL_KEY_WHITE_COUNT,
437     &game.panel.key_white_count,
438     TYPE_INTEGER,
439   },
440   {
441     GAME_PANEL_SCORE,
442     &game.panel.score,
443     TYPE_INTEGER,
444   },
445   {
446     GAME_PANEL_HIGHSCORE,
447     &game.panel.highscore,
448     TYPE_INTEGER,
449   },
450   {
451     GAME_PANEL_TIME,
452     &game.panel.time,
453     TYPE_INTEGER,
454   },
455   {
456     GAME_PANEL_TIME_HH,
457     &game.panel.time_hh,
458     TYPE_INTEGER,
459   },
460   {
461     GAME_PANEL_TIME_MM,
462     &game.panel.time_mm,
463     TYPE_INTEGER,
464   },
465   {
466     GAME_PANEL_TIME_SS,
467     &game.panel.time_ss,
468     TYPE_INTEGER,
469   },
470   {
471     GAME_PANEL_SHIELD_NORMAL,
472     &game.panel.shield_normal,
473     TYPE_ELEMENT,
474   },
475   {
476     GAME_PANEL_SHIELD_NORMAL_TIME,
477     &game.panel.shield_normal_time,
478     TYPE_INTEGER,
479   },
480   {
481     GAME_PANEL_SHIELD_DEADLY,
482     &game.panel.shield_deadly,
483     TYPE_ELEMENT,
484   },
485   {
486     GAME_PANEL_SHIELD_DEADLY_TIME,
487     &game.panel.shield_deadly_time,
488     TYPE_INTEGER,
489   },
490   {
491     GAME_PANEL_EXIT,
492     &game.panel.exit,
493     TYPE_ELEMENT,
494   },
495   {
496     GAME_PANEL_EMC_MAGIC_BALL,
497     &game.panel.emc_magic_ball,
498     TYPE_ELEMENT,
499   },
500   {
501     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
502     &game.panel.emc_magic_ball_switch,
503     TYPE_ELEMENT,
504   },
505   {
506     GAME_PANEL_LIGHT_SWITCH,
507     &game.panel.light_switch,
508     TYPE_ELEMENT,
509   },
510   {
511     GAME_PANEL_LIGHT_SWITCH_TIME,
512     &game.panel.light_switch_time,
513     TYPE_INTEGER,
514   },
515   {
516     GAME_PANEL_TIMEGATE_SWITCH,
517     &game.panel.timegate_switch,
518     TYPE_ELEMENT,
519   },
520   {
521     GAME_PANEL_TIMEGATE_SWITCH_TIME,
522     &game.panel.timegate_switch_time,
523     TYPE_INTEGER,
524   },
525   {
526     GAME_PANEL_SWITCHGATE_SWITCH,
527     &game.panel.switchgate_switch,
528     TYPE_ELEMENT,
529   },
530   {
531     GAME_PANEL_EMC_LENSES,
532     &game.panel.emc_lenses,
533     TYPE_ELEMENT,
534   },
535   {
536     GAME_PANEL_EMC_LENSES_TIME,
537     &game.panel.emc_lenses_time,
538     TYPE_INTEGER,
539   },
540   {
541     GAME_PANEL_EMC_MAGNIFIER,
542     &game.panel.emc_magnifier,
543     TYPE_ELEMENT,
544   },
545   {
546     GAME_PANEL_EMC_MAGNIFIER_TIME,
547     &game.panel.emc_magnifier_time,
548     TYPE_INTEGER,
549   },
550   {
551     GAME_PANEL_BALLOON_SWITCH,
552     &game.panel.balloon_switch,
553     TYPE_ELEMENT,
554   },
555   {
556     GAME_PANEL_DYNABOMB_NUMBER,
557     &game.panel.dynabomb_number,
558     TYPE_INTEGER,
559   },
560   {
561     GAME_PANEL_DYNABOMB_SIZE,
562     &game.panel.dynabomb_size,
563     TYPE_INTEGER,
564   },
565   {
566     GAME_PANEL_DYNABOMB_POWER,
567     &game.panel.dynabomb_power,
568     TYPE_ELEMENT,
569   },
570   {
571     GAME_PANEL_PENGUINS,
572     &game.panel.penguins,
573     TYPE_INTEGER,
574   },
575   {
576     GAME_PANEL_SOKOBAN_OBJECTS,
577     &game.panel.sokoban_objects,
578     TYPE_INTEGER,
579   },
580   {
581     GAME_PANEL_SOKOBAN_FIELDS,
582     &game.panel.sokoban_fields,
583     TYPE_INTEGER,
584   },
585   {
586     GAME_PANEL_ROBOT_WHEEL,
587     &game.panel.robot_wheel,
588     TYPE_ELEMENT,
589   },
590   {
591     GAME_PANEL_CONVEYOR_BELT_1,
592     &game.panel.conveyor_belt[0],
593     TYPE_ELEMENT,
594   },
595   {
596     GAME_PANEL_CONVEYOR_BELT_2,
597     &game.panel.conveyor_belt[1],
598     TYPE_ELEMENT,
599   },
600   {
601     GAME_PANEL_CONVEYOR_BELT_3,
602     &game.panel.conveyor_belt[2],
603     TYPE_ELEMENT,
604   },
605   {
606     GAME_PANEL_CONVEYOR_BELT_4,
607     &game.panel.conveyor_belt[3],
608     TYPE_ELEMENT,
609   },
610   {
611     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
612     &game.panel.conveyor_belt_switch[0],
613     TYPE_ELEMENT,
614   },
615   {
616     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
617     &game.panel.conveyor_belt_switch[1],
618     TYPE_ELEMENT,
619   },
620   {
621     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
622     &game.panel.conveyor_belt_switch[2],
623     TYPE_ELEMENT,
624   },
625   {
626     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
627     &game.panel.conveyor_belt_switch[3],
628     TYPE_ELEMENT,
629   },
630   {
631     GAME_PANEL_MAGIC_WALL,
632     &game.panel.magic_wall,
633     TYPE_ELEMENT,
634   },
635   {
636     GAME_PANEL_MAGIC_WALL_TIME,
637     &game.panel.magic_wall_time,
638     TYPE_INTEGER,
639   },
640   {
641     GAME_PANEL_GRAVITY_STATE,
642     &game.panel.gravity_state,
643     TYPE_STRING,
644   },
645   {
646     GAME_PANEL_GRAPHIC_1,
647     &game.panel.graphic[0],
648     TYPE_ELEMENT,
649   },
650   {
651     GAME_PANEL_GRAPHIC_2,
652     &game.panel.graphic[1],
653     TYPE_ELEMENT,
654   },
655   {
656     GAME_PANEL_GRAPHIC_3,
657     &game.panel.graphic[2],
658     TYPE_ELEMENT,
659   },
660   {
661     GAME_PANEL_GRAPHIC_4,
662     &game.panel.graphic[3],
663     TYPE_ELEMENT,
664   },
665   {
666     GAME_PANEL_GRAPHIC_5,
667     &game.panel.graphic[4],
668     TYPE_ELEMENT,
669   },
670   {
671     GAME_PANEL_GRAPHIC_6,
672     &game.panel.graphic[5],
673     TYPE_ELEMENT,
674   },
675   {
676     GAME_PANEL_GRAPHIC_7,
677     &game.panel.graphic[6],
678     TYPE_ELEMENT,
679   },
680   {
681     GAME_PANEL_GRAPHIC_8,
682     &game.panel.graphic[7],
683     TYPE_ELEMENT,
684   },
685   {
686     GAME_PANEL_ELEMENT_1,
687     &game.panel.element[0],
688     TYPE_ELEMENT,
689   },
690   {
691     GAME_PANEL_ELEMENT_2,
692     &game.panel.element[1],
693     TYPE_ELEMENT,
694   },
695   {
696     GAME_PANEL_ELEMENT_3,
697     &game.panel.element[2],
698     TYPE_ELEMENT,
699   },
700   {
701     GAME_PANEL_ELEMENT_4,
702     &game.panel.element[3],
703     TYPE_ELEMENT,
704   },
705   {
706     GAME_PANEL_ELEMENT_5,
707     &game.panel.element[4],
708     TYPE_ELEMENT,
709   },
710   {
711     GAME_PANEL_ELEMENT_6,
712     &game.panel.element[5],
713     TYPE_ELEMENT,
714   },
715   {
716     GAME_PANEL_ELEMENT_7,
717     &game.panel.element[6],
718     TYPE_ELEMENT,
719   },
720   {
721     GAME_PANEL_ELEMENT_8,
722     &game.panel.element[7],
723     TYPE_ELEMENT,
724   },
725   {
726     GAME_PANEL_ELEMENT_COUNT_1,
727     &game.panel.element_count[0],
728     TYPE_INTEGER,
729   },
730   {
731     GAME_PANEL_ELEMENT_COUNT_2,
732     &game.panel.element_count[1],
733     TYPE_INTEGER,
734   },
735   {
736     GAME_PANEL_ELEMENT_COUNT_3,
737     &game.panel.element_count[2],
738     TYPE_INTEGER,
739   },
740   {
741     GAME_PANEL_ELEMENT_COUNT_4,
742     &game.panel.element_count[3],
743     TYPE_INTEGER,
744   },
745   {
746     GAME_PANEL_ELEMENT_COUNT_5,
747     &game.panel.element_count[4],
748     TYPE_INTEGER,
749   },
750   {
751     GAME_PANEL_ELEMENT_COUNT_6,
752     &game.panel.element_count[5],
753     TYPE_INTEGER,
754   },
755   {
756     GAME_PANEL_ELEMENT_COUNT_7,
757     &game.panel.element_count[6],
758     TYPE_INTEGER,
759   },
760   {
761     GAME_PANEL_ELEMENT_COUNT_8,
762     &game.panel.element_count[7],
763     TYPE_INTEGER,
764   },
765   {
766     GAME_PANEL_CE_SCORE_1,
767     &game.panel.ce_score[0],
768     TYPE_INTEGER,
769   },
770   {
771     GAME_PANEL_CE_SCORE_2,
772     &game.panel.ce_score[1],
773     TYPE_INTEGER,
774   },
775   {
776     GAME_PANEL_CE_SCORE_3,
777     &game.panel.ce_score[2],
778     TYPE_INTEGER,
779   },
780   {
781     GAME_PANEL_CE_SCORE_4,
782     &game.panel.ce_score[3],
783     TYPE_INTEGER,
784   },
785   {
786     GAME_PANEL_CE_SCORE_5,
787     &game.panel.ce_score[4],
788     TYPE_INTEGER,
789   },
790   {
791     GAME_PANEL_CE_SCORE_6,
792     &game.panel.ce_score[5],
793     TYPE_INTEGER,
794   },
795   {
796     GAME_PANEL_CE_SCORE_7,
797     &game.panel.ce_score[6],
798     TYPE_INTEGER,
799   },
800   {
801     GAME_PANEL_CE_SCORE_8,
802     &game.panel.ce_score[7],
803     TYPE_INTEGER,
804   },
805   {
806     GAME_PANEL_CE_SCORE_1_ELEMENT,
807     &game.panel.ce_score_element[0],
808     TYPE_ELEMENT,
809   },
810   {
811     GAME_PANEL_CE_SCORE_2_ELEMENT,
812     &game.panel.ce_score_element[1],
813     TYPE_ELEMENT,
814   },
815   {
816     GAME_PANEL_CE_SCORE_3_ELEMENT,
817     &game.panel.ce_score_element[2],
818     TYPE_ELEMENT,
819   },
820   {
821     GAME_PANEL_CE_SCORE_4_ELEMENT,
822     &game.panel.ce_score_element[3],
823     TYPE_ELEMENT,
824   },
825   {
826     GAME_PANEL_CE_SCORE_5_ELEMENT,
827     &game.panel.ce_score_element[4],
828     TYPE_ELEMENT,
829   },
830   {
831     GAME_PANEL_CE_SCORE_6_ELEMENT,
832     &game.panel.ce_score_element[5],
833     TYPE_ELEMENT,
834   },
835   {
836     GAME_PANEL_CE_SCORE_7_ELEMENT,
837     &game.panel.ce_score_element[6],
838     TYPE_ELEMENT,
839   },
840   {
841     GAME_PANEL_CE_SCORE_8_ELEMENT,
842     &game.panel.ce_score_element[7],
843     TYPE_ELEMENT,
844   },
845   {
846     GAME_PANEL_PLAYER_NAME,
847     &game.panel.player_name,
848     TYPE_STRING,
849   },
850   {
851     GAME_PANEL_LEVEL_NAME,
852     &game.panel.level_name,
853     TYPE_STRING,
854   },
855   {
856     GAME_PANEL_LEVEL_AUTHOR,
857     &game.panel.level_author,
858     TYPE_STRING,
859   },
860
861   {
862     -1,
863     NULL,
864     -1,
865   }
866 };
867 #endif
868
869
870 /* values for delayed check of falling and moving elements and for collision */
871 #define CHECK_DELAY_MOVING      3
872 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
873 #define CHECK_DELAY_COLLISION   2
874 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
875
876 /* values for initial player move delay (initial delay counter value) */
877 #define INITIAL_MOVE_DELAY_OFF  -1
878 #define INITIAL_MOVE_DELAY_ON   0
879
880 /* values for player movement speed (which is in fact a delay value) */
881 #define MOVE_DELAY_MIN_SPEED    32
882 #define MOVE_DELAY_NORMAL_SPEED 8
883 #define MOVE_DELAY_HIGH_SPEED   4
884 #define MOVE_DELAY_MAX_SPEED    1
885
886 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
887 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
888
889 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
890 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
891
892 /* values for other actions */
893 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
894 #define MOVE_STEPSIZE_MIN       (1)
895 #define MOVE_STEPSIZE_MAX       (TILEX)
896
897 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
898 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
899
900 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
901
902 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
903                                  RND(element_info[e].push_delay_random))
904 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
905                                  RND(element_info[e].drop_delay_random))
906 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
907                                  RND(element_info[e].move_delay_random))
908 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
909                                     (element_info[e].move_delay_random))
910 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
911                                  RND(element_info[e].ce_value_random_initial))
912 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
913 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
914                                  RND((c)->delay_random * (c)->delay_frames))
915 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
916                                  RND((c)->delay_random))
917
918
919 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
920          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
921
922 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
923         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
924          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
925          (be) + (e) - EL_SELF)
926
927 #define GET_PLAYER_FROM_BITS(p)                                         \
928         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
929
930 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
931         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
932          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
933          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
934          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
935          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
936          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
937          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
938          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
939          (e))
940
941 #define CAN_GROW_INTO(e)                                                \
942         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
943
944 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
945                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
946                                         (condition)))
947
948 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
949                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
950                                         (CAN_MOVE_INTO_ACID(e) &&       \
951                                          Feld[x][y] == EL_ACID) ||      \
952                                         (condition)))
953
954 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
955                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
956                                         (CAN_MOVE_INTO_ACID(e) &&       \
957                                          Feld[x][y] == EL_ACID) ||      \
958                                         (condition)))
959
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
961                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
962                                         (condition) ||                  \
963                                         (CAN_MOVE_INTO_ACID(e) &&       \
964                                          Feld[x][y] == EL_ACID) ||      \
965                                         (DONT_COLLIDE_WITH(e) &&        \
966                                          IS_PLAYER(x, y) &&             \
967                                          !PLAYER_ENEMY_PROTECTED(x, y))))
968
969 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
971
972 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
973         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
974
975 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
977
978 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
979         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
980                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
981
982 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
984
985 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
987
988 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
989         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
990
991 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
992         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
993
994 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
995         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
996
997 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
998         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
999                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
1000                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1001                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1002                                                  IS_FOOD_PENGUIN(Feld[x][y])))
1003 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
1004         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1005
1006 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1007         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1008
1009 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1010         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1011
1012 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1013         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
1014                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1015
1016 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1017
1018 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1019                 (!IS_PLAYER(x, y) &&                                    \
1020                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1021
1022 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1023         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1024
1025 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1026 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1027
1028 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1029 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1030 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1031 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1032
1033 /* game button identifiers */
1034 #define GAME_CTRL_ID_STOP               0
1035 #define GAME_CTRL_ID_PAUSE              1
1036 #define GAME_CTRL_ID_PLAY               2
1037 #define SOUND_CTRL_ID_MUSIC             3
1038 #define SOUND_CTRL_ID_LOOPS             4
1039 #define SOUND_CTRL_ID_SIMPLE            5
1040
1041 #define NUM_GAME_BUTTONS                6
1042
1043
1044 /* forward declaration for internal use */
1045
1046 static void CreateField(int, int, int);
1047
1048 static void ResetGfxAnimation(int, int);
1049
1050 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1051 static void AdvanceFrameAndPlayerCounters(int);
1052
1053 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1054 static boolean MovePlayer(struct PlayerInfo *, int, int);
1055 static void ScrollPlayer(struct PlayerInfo *, int);
1056 static void ScrollScreen(struct PlayerInfo *, int);
1057
1058 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1059 static boolean DigFieldByCE(int, int, int);
1060 static boolean SnapField(struct PlayerInfo *, int, int);
1061 static boolean DropElement(struct PlayerInfo *);
1062
1063 static void InitBeltMovement(void);
1064 static void CloseAllOpenTimegates(void);
1065 static void CheckGravityMovement(struct PlayerInfo *);
1066 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1067 static void KillPlayerUnlessEnemyProtected(int, int);
1068 static void KillPlayerUnlessExplosionProtected(int, int);
1069
1070 static void TestIfPlayerTouchesCustomElement(int, int);
1071 static void TestIfElementTouchesCustomElement(int, int);
1072 static void TestIfElementHitsCustomElement(int, int, int);
1073 #if 0
1074 static void TestIfElementSmashesCustomElement(int, int, int);
1075 #endif
1076
1077 static void HandleElementChange(int, int, int);
1078 static void ExecuteCustomElementAction(int, int, int, int);
1079 static boolean ChangeElement(int, int, int, int);
1080
1081 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1082 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1083         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1084 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1085         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1086 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1087         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1088 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1089         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1090
1091 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1092 #define CheckElementChange(x, y, e, te, ev)                             \
1093         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1094 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1095         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1096 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1097         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1098
1099 static void PlayLevelSound(int, int, int);
1100 static void PlayLevelSoundNearest(int, int, int);
1101 static void PlayLevelSoundAction(int, int, int);
1102 static void PlayLevelSoundElementAction(int, int, int, int);
1103 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1104 static void PlayLevelSoundActionIfLoop(int, int, int);
1105 static void StopLevelSoundActionIfLoop(int, int, int);
1106 static void PlayLevelMusic();
1107
1108 static void MapGameButtons();
1109 static void HandleGameButtons(struct GadgetInfo *);
1110
1111 int AmoebeNachbarNr(int, int);
1112 void AmoebeUmwandeln(int, int);
1113 void ContinueMoving(int, int);
1114 void Bang(int, int);
1115 void InitMovDir(int, int);
1116 void InitAmoebaNr(int, int);
1117 int NewHiScore(void);
1118
1119 void TestIfGoodThingHitsBadThing(int, int, int);
1120 void TestIfBadThingHitsGoodThing(int, int, int);
1121 void TestIfPlayerTouchesBadThing(int, int);
1122 void TestIfPlayerRunsIntoBadThing(int, int, int);
1123 void TestIfBadThingTouchesPlayer(int, int);
1124 void TestIfBadThingRunsIntoPlayer(int, int, int);
1125 void TestIfFriendTouchesBadThing(int, int);
1126 void TestIfBadThingTouchesFriend(int, int);
1127 void TestIfBadThingTouchesOtherBadThing(int, int);
1128
1129 void KillPlayer(struct PlayerInfo *);
1130 void BuryPlayer(struct PlayerInfo *);
1131 void RemovePlayer(struct PlayerInfo *);
1132
1133 static int getInvisibleActiveFromInvisibleElement(int);
1134 static int getInvisibleFromInvisibleActiveElement(int);
1135
1136 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1137
1138 /* for detection of endless loops, caused by custom element programming */
1139 /* (using maximal playfield width x 10 is just a rough approximation) */
1140 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1141
1142 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1143 {                                                                       \
1144   if (recursion_loop_detected)                                          \
1145     return (rc);                                                        \
1146                                                                         \
1147   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1148   {                                                                     \
1149     recursion_loop_detected = TRUE;                                     \
1150     recursion_loop_element = (e);                                       \
1151   }                                                                     \
1152                                                                         \
1153   recursion_loop_depth++;                                               \
1154 }
1155
1156 #define RECURSION_LOOP_DETECTION_END()                                  \
1157 {                                                                       \
1158   recursion_loop_depth--;                                               \
1159 }
1160
1161 static int recursion_loop_depth;
1162 static boolean recursion_loop_detected;
1163 static boolean recursion_loop_element;
1164
1165
1166 /* ------------------------------------------------------------------------- */
1167 /* definition of elements that automatically change to other elements after  */
1168 /* a specified time, eventually calling a function when changing             */
1169 /* ------------------------------------------------------------------------- */
1170
1171 /* forward declaration for changer functions */
1172 static void InitBuggyBase(int, int);
1173 static void WarnBuggyBase(int, int);
1174
1175 static void InitTrap(int, int);
1176 static void ActivateTrap(int, int);
1177 static void ChangeActiveTrap(int, int);
1178
1179 static void InitRobotWheel(int, int);
1180 static void RunRobotWheel(int, int);
1181 static void StopRobotWheel(int, int);
1182
1183 static void InitTimegateWheel(int, int);
1184 static void RunTimegateWheel(int, int);
1185
1186 static void InitMagicBallDelay(int, int);
1187 static void ActivateMagicBall(int, int);
1188
1189 struct ChangingElementInfo
1190 {
1191   int element;
1192   int target_element;
1193   int change_delay;
1194   void (*pre_change_function)(int x, int y);
1195   void (*change_function)(int x, int y);
1196   void (*post_change_function)(int x, int y);
1197 };
1198
1199 static struct ChangingElementInfo change_delay_list[] =
1200 {
1201   {
1202     EL_NUT_BREAKING,
1203     EL_EMERALD,
1204     6,
1205     NULL,
1206     NULL,
1207     NULL
1208   },
1209   {
1210     EL_PEARL_BREAKING,
1211     EL_EMPTY,
1212     8,
1213     NULL,
1214     NULL,
1215     NULL
1216   },
1217   {
1218     EL_EXIT_OPENING,
1219     EL_EXIT_OPEN,
1220     29,
1221     NULL,
1222     NULL,
1223     NULL
1224   },
1225   {
1226     EL_EXIT_CLOSING,
1227     EL_EXIT_CLOSED,
1228     29,
1229     NULL,
1230     NULL,
1231     NULL
1232   },
1233   {
1234     EL_STEEL_EXIT_OPENING,
1235     EL_STEEL_EXIT_OPEN,
1236     29,
1237     NULL,
1238     NULL,
1239     NULL
1240   },
1241   {
1242     EL_STEEL_EXIT_CLOSING,
1243     EL_STEEL_EXIT_CLOSED,
1244     29,
1245     NULL,
1246     NULL,
1247     NULL
1248   },
1249   {
1250     EL_EM_EXIT_OPENING,
1251     EL_EM_EXIT_OPEN,
1252     29,
1253     NULL,
1254     NULL,
1255     NULL
1256   },
1257   {
1258     EL_EM_EXIT_CLOSING,
1259 #if 1
1260     EL_EMPTY,
1261 #else
1262     EL_EM_EXIT_CLOSED,
1263 #endif
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_EM_STEEL_EXIT_OPENING,
1271     EL_EM_STEEL_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_EM_STEEL_EXIT_CLOSING,
1279 #if 1
1280     EL_STEELWALL,
1281 #else
1282     EL_EM_STEEL_EXIT_CLOSED,
1283 #endif
1284     29,
1285     NULL,
1286     NULL,
1287     NULL
1288   },
1289   {
1290     EL_SP_EXIT_OPENING,
1291     EL_SP_EXIT_OPEN,
1292     29,
1293     NULL,
1294     NULL,
1295     NULL
1296   },
1297   {
1298     EL_SP_EXIT_CLOSING,
1299     EL_SP_EXIT_CLOSED,
1300     29,
1301     NULL,
1302     NULL,
1303     NULL
1304   },
1305   {
1306     EL_SWITCHGATE_OPENING,
1307     EL_SWITCHGATE_OPEN,
1308     29,
1309     NULL,
1310     NULL,
1311     NULL
1312   },
1313   {
1314     EL_SWITCHGATE_CLOSING,
1315     EL_SWITCHGATE_CLOSED,
1316     29,
1317     NULL,
1318     NULL,
1319     NULL
1320   },
1321   {
1322     EL_TIMEGATE_OPENING,
1323     EL_TIMEGATE_OPEN,
1324     29,
1325     NULL,
1326     NULL,
1327     NULL
1328   },
1329   {
1330     EL_TIMEGATE_CLOSING,
1331     EL_TIMEGATE_CLOSED,
1332     29,
1333     NULL,
1334     NULL,
1335     NULL
1336   },
1337
1338   {
1339     EL_ACID_SPLASH_LEFT,
1340     EL_EMPTY,
1341     8,
1342     NULL,
1343     NULL,
1344     NULL
1345   },
1346   {
1347     EL_ACID_SPLASH_RIGHT,
1348     EL_EMPTY,
1349     8,
1350     NULL,
1351     NULL,
1352     NULL
1353   },
1354   {
1355     EL_SP_BUGGY_BASE,
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     0,
1358     InitBuggyBase,
1359     NULL,
1360     NULL
1361   },
1362   {
1363     EL_SP_BUGGY_BASE_ACTIVATING,
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     0,
1366     InitBuggyBase,
1367     NULL,
1368     NULL
1369   },
1370   {
1371     EL_SP_BUGGY_BASE_ACTIVE,
1372     EL_SP_BUGGY_BASE,
1373     0,
1374     InitBuggyBase,
1375     WarnBuggyBase,
1376     NULL
1377   },
1378   {
1379     EL_TRAP,
1380     EL_TRAP_ACTIVE,
1381     0,
1382     InitTrap,
1383     NULL,
1384     ActivateTrap
1385   },
1386   {
1387     EL_TRAP_ACTIVE,
1388     EL_TRAP,
1389     31,
1390     NULL,
1391     ChangeActiveTrap,
1392     NULL
1393   },
1394   {
1395     EL_ROBOT_WHEEL_ACTIVE,
1396     EL_ROBOT_WHEEL,
1397     0,
1398     InitRobotWheel,
1399     RunRobotWheel,
1400     StopRobotWheel
1401   },
1402   {
1403     EL_TIMEGATE_SWITCH_ACTIVE,
1404     EL_TIMEGATE_SWITCH,
1405     0,
1406     InitTimegateWheel,
1407     RunTimegateWheel,
1408     NULL
1409   },
1410   {
1411     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1412     EL_DC_TIMEGATE_SWITCH,
1413     0,
1414     InitTimegateWheel,
1415     RunTimegateWheel,
1416     NULL
1417   },
1418   {
1419     EL_EMC_MAGIC_BALL_ACTIVE,
1420     EL_EMC_MAGIC_BALL_ACTIVE,
1421     0,
1422     InitMagicBallDelay,
1423     NULL,
1424     ActivateMagicBall
1425   },
1426   {
1427     EL_EMC_SPRING_BUMPER_ACTIVE,
1428     EL_EMC_SPRING_BUMPER,
1429     8,
1430     NULL,
1431     NULL,
1432     NULL
1433   },
1434   {
1435     EL_DIAGONAL_SHRINKING,
1436     EL_UNDEFINED,
1437     0,
1438     NULL,
1439     NULL,
1440     NULL
1441   },
1442   {
1443     EL_DIAGONAL_GROWING,
1444     EL_UNDEFINED,
1445     0,
1446     NULL,
1447     NULL,
1448     NULL,
1449   },
1450
1451   {
1452     EL_UNDEFINED,
1453     EL_UNDEFINED,
1454     -1,
1455     NULL,
1456     NULL,
1457     NULL
1458   }
1459 };
1460
1461 struct
1462 {
1463   int element;
1464   int push_delay_fixed, push_delay_random;
1465 }
1466 push_delay_list[] =
1467 {
1468   { EL_SPRING,                  0, 0 },
1469   { EL_BALLOON,                 0, 0 },
1470
1471   { EL_SOKOBAN_OBJECT,          2, 0 },
1472   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1473   { EL_SATELLITE,               2, 0 },
1474   { EL_SP_DISK_YELLOW,          2, 0 },
1475
1476   { EL_UNDEFINED,               0, 0 },
1477 };
1478
1479 struct
1480 {
1481   int element;
1482   int move_stepsize;
1483 }
1484 move_stepsize_list[] =
1485 {
1486   { EL_AMOEBA_DROP,             2 },
1487   { EL_AMOEBA_DROPPING,         2 },
1488   { EL_QUICKSAND_FILLING,       1 },
1489   { EL_QUICKSAND_EMPTYING,      1 },
1490   { EL_QUICKSAND_FAST_FILLING,  2 },
1491   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1492   { EL_MAGIC_WALL_FILLING,      2 },
1493   { EL_MAGIC_WALL_EMPTYING,     2 },
1494   { EL_BD_MAGIC_WALL_FILLING,   2 },
1495   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1496   { EL_DC_MAGIC_WALL_FILLING,   2 },
1497   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1498
1499   { EL_UNDEFINED,               0 },
1500 };
1501
1502 struct
1503 {
1504   int element;
1505   int count;
1506 }
1507 collect_count_list[] =
1508 {
1509   { EL_EMERALD,                 1 },
1510   { EL_BD_DIAMOND,              1 },
1511   { EL_EMERALD_YELLOW,          1 },
1512   { EL_EMERALD_RED,             1 },
1513   { EL_EMERALD_PURPLE,          1 },
1514   { EL_DIAMOND,                 3 },
1515   { EL_SP_INFOTRON,             1 },
1516   { EL_PEARL,                   5 },
1517   { EL_CRYSTAL,                 8 },
1518
1519   { EL_UNDEFINED,               0 },
1520 };
1521
1522 struct
1523 {
1524   int element;
1525   int direction;
1526 }
1527 access_direction_list[] =
1528 {
1529   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1530   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1531   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1532   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1533   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1534   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1535   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1536   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1537   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1538   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1539   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1540
1541   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1542   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1543   { EL_SP_PORT_UP,                                                   MV_DOWN },
1544   { EL_SP_PORT_DOWN,                                         MV_UP           },
1545   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1546   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1547   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1548   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1549   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1550   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1551   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1552   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1553   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1554   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1555   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1556   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1557   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1558   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1559   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1560
1561   { EL_UNDEFINED,                       MV_NONE                              }
1562 };
1563
1564 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1565
1566 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1567 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1568 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1569                                  IS_JUST_CHANGING(x, y))
1570
1571 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1572
1573 /* static variables for playfield scan mode (scanning forward or backward) */
1574 static int playfield_scan_start_x = 0;
1575 static int playfield_scan_start_y = 0;
1576 static int playfield_scan_delta_x = 1;
1577 static int playfield_scan_delta_y = 1;
1578
1579 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1580                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1581                                      (y) += playfield_scan_delta_y)     \
1582                                 for ((x) = playfield_scan_start_x;      \
1583                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1584                                      (x) += playfield_scan_delta_x)
1585
1586 #ifdef DEBUG
1587 void DEBUG_SetMaximumDynamite()
1588 {
1589   int i;
1590
1591   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1592     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1593       local_player->inventory_element[local_player->inventory_size++] =
1594         EL_DYNAMITE;
1595 }
1596 #endif
1597
1598 static void InitPlayfieldScanModeVars()
1599 {
1600   if (game.use_reverse_scan_direction)
1601   {
1602     playfield_scan_start_x = lev_fieldx - 1;
1603     playfield_scan_start_y = lev_fieldy - 1;
1604
1605     playfield_scan_delta_x = -1;
1606     playfield_scan_delta_y = -1;
1607   }
1608   else
1609   {
1610     playfield_scan_start_x = 0;
1611     playfield_scan_start_y = 0;
1612
1613     playfield_scan_delta_x = 1;
1614     playfield_scan_delta_y = 1;
1615   }
1616 }
1617
1618 static void InitPlayfieldScanMode(int mode)
1619 {
1620   game.use_reverse_scan_direction =
1621     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1622
1623   InitPlayfieldScanModeVars();
1624 }
1625
1626 static int get_move_delay_from_stepsize(int move_stepsize)
1627 {
1628   move_stepsize =
1629     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1630
1631   /* make sure that stepsize value is always a power of 2 */
1632   move_stepsize = (1 << log_2(move_stepsize));
1633
1634   return TILEX / move_stepsize;
1635 }
1636
1637 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1638                                boolean init_game)
1639 {
1640   int player_nr = player->index_nr;
1641   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1642   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1643
1644   /* do no immediately change move delay -- the player might just be moving */
1645   player->move_delay_value_next = move_delay;
1646
1647   /* information if player can move must be set separately */
1648   player->cannot_move = cannot_move;
1649
1650   if (init_game)
1651   {
1652     player->move_delay       = game.initial_move_delay[player_nr];
1653     player->move_delay_value = game.initial_move_delay_value[player_nr];
1654
1655     player->move_delay_value_next = -1;
1656
1657     player->move_delay_reset_counter = 0;
1658   }
1659 }
1660
1661 void GetPlayerConfig()
1662 {
1663   GameFrameDelay = setup.game_frame_delay;
1664
1665   if (!audio.sound_available)
1666     setup.sound_simple = FALSE;
1667
1668   if (!audio.loops_available)
1669     setup.sound_loops = FALSE;
1670
1671   if (!audio.music_available)
1672     setup.sound_music = FALSE;
1673
1674   if (!video.fullscreen_available)
1675     setup.fullscreen = FALSE;
1676
1677   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1678
1679   SetAudioMode(setup.sound);
1680   InitJoysticks();
1681 }
1682
1683 int GetElementFromGroupElement(int element)
1684 {
1685   if (IS_GROUP_ELEMENT(element))
1686   {
1687     struct ElementGroupInfo *group = element_info[element].group;
1688     int last_anim_random_frame = gfx.anim_random_frame;
1689     int element_pos;
1690
1691     if (group->choice_mode == ANIM_RANDOM)
1692       gfx.anim_random_frame = RND(group->num_elements_resolved);
1693
1694     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1695                                     group->choice_mode, 0,
1696                                     group->choice_pos);
1697
1698     if (group->choice_mode == ANIM_RANDOM)
1699       gfx.anim_random_frame = last_anim_random_frame;
1700
1701     group->choice_pos++;
1702
1703     element = group->element_resolved[element_pos];
1704   }
1705
1706   return element;
1707 }
1708
1709 static void InitPlayerField(int x, int y, int element, boolean init_game)
1710 {
1711   if (element == EL_SP_MURPHY)
1712   {
1713     if (init_game)
1714     {
1715       if (stored_player[0].present)
1716       {
1717         Feld[x][y] = EL_SP_MURPHY_CLONE;
1718
1719         return;
1720       }
1721       else
1722       {
1723         stored_player[0].use_murphy = TRUE;
1724
1725         if (!level.use_artwork_element[0])
1726           stored_player[0].artwork_element = EL_SP_MURPHY;
1727       }
1728
1729       Feld[x][y] = EL_PLAYER_1;
1730     }
1731   }
1732
1733   if (init_game)
1734   {
1735     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1736     int jx = player->jx, jy = player->jy;
1737
1738     player->present = TRUE;
1739
1740     player->block_last_field = (element == EL_SP_MURPHY ?
1741                                 level.sp_block_last_field :
1742                                 level.block_last_field);
1743
1744     /* ---------- initialize player's last field block delay --------------- */
1745
1746     /* always start with reliable default value (no adjustment needed) */
1747     player->block_delay_adjustment = 0;
1748
1749     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1750     if (player->block_last_field && element == EL_SP_MURPHY)
1751       player->block_delay_adjustment = 1;
1752
1753     /* special case 2: in game engines before 3.1.1, blocking was different */
1754     if (game.use_block_last_field_bug)
1755       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1756
1757     if (!options.network || player->connected)
1758     {
1759       player->active = TRUE;
1760
1761       /* remove potentially duplicate players */
1762       if (StorePlayer[jx][jy] == Feld[x][y])
1763         StorePlayer[jx][jy] = 0;
1764
1765       StorePlayer[x][y] = Feld[x][y];
1766
1767       if (options.debug)
1768       {
1769         printf("Player %d activated.\n", player->element_nr);
1770         printf("[Local player is %d and currently %s.]\n",
1771                local_player->element_nr,
1772                local_player->active ? "active" : "not active");
1773       }
1774     }
1775
1776     Feld[x][y] = EL_EMPTY;
1777
1778     player->jx = player->last_jx = x;
1779     player->jy = player->last_jy = y;
1780   }
1781 }
1782
1783 static void InitField(int x, int y, boolean init_game)
1784 {
1785   int element = Feld[x][y];
1786
1787   switch (element)
1788   {
1789     case EL_SP_MURPHY:
1790     case EL_PLAYER_1:
1791     case EL_PLAYER_2:
1792     case EL_PLAYER_3:
1793     case EL_PLAYER_4:
1794       InitPlayerField(x, y, element, init_game);
1795       break;
1796
1797     case EL_SOKOBAN_FIELD_PLAYER:
1798       element = Feld[x][y] = EL_PLAYER_1;
1799       InitField(x, y, init_game);
1800
1801       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1802       InitField(x, y, init_game);
1803       break;
1804
1805     case EL_SOKOBAN_FIELD_EMPTY:
1806       local_player->sokobanfields_still_needed++;
1807       break;
1808
1809     case EL_STONEBLOCK:
1810       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1811         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1812       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1813         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1814       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1815         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1816       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1817         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1818       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1819         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1820       break;
1821
1822     case EL_BUG:
1823     case EL_BUG_RIGHT:
1824     case EL_BUG_UP:
1825     case EL_BUG_LEFT:
1826     case EL_BUG_DOWN:
1827     case EL_SPACESHIP:
1828     case EL_SPACESHIP_RIGHT:
1829     case EL_SPACESHIP_UP:
1830     case EL_SPACESHIP_LEFT:
1831     case EL_SPACESHIP_DOWN:
1832     case EL_BD_BUTTERFLY:
1833     case EL_BD_BUTTERFLY_RIGHT:
1834     case EL_BD_BUTTERFLY_UP:
1835     case EL_BD_BUTTERFLY_LEFT:
1836     case EL_BD_BUTTERFLY_DOWN:
1837     case EL_BD_FIREFLY:
1838     case EL_BD_FIREFLY_RIGHT:
1839     case EL_BD_FIREFLY_UP:
1840     case EL_BD_FIREFLY_LEFT:
1841     case EL_BD_FIREFLY_DOWN:
1842     case EL_PACMAN_RIGHT:
1843     case EL_PACMAN_UP:
1844     case EL_PACMAN_LEFT:
1845     case EL_PACMAN_DOWN:
1846     case EL_YAMYAM:
1847     case EL_YAMYAM_LEFT:
1848     case EL_YAMYAM_RIGHT:
1849     case EL_YAMYAM_UP:
1850     case EL_YAMYAM_DOWN:
1851     case EL_DARK_YAMYAM:
1852     case EL_ROBOT:
1853     case EL_PACMAN:
1854     case EL_SP_SNIKSNAK:
1855     case EL_SP_ELECTRON:
1856     case EL_MOLE:
1857     case EL_MOLE_LEFT:
1858     case EL_MOLE_RIGHT:
1859     case EL_MOLE_UP:
1860     case EL_MOLE_DOWN:
1861       InitMovDir(x, y);
1862       break;
1863
1864     case EL_AMOEBA_FULL:
1865     case EL_BD_AMOEBA:
1866       InitAmoebaNr(x, y);
1867       break;
1868
1869     case EL_AMOEBA_DROP:
1870       if (y == lev_fieldy - 1)
1871       {
1872         Feld[x][y] = EL_AMOEBA_GROWING;
1873         Store[x][y] = EL_AMOEBA_WET;
1874       }
1875       break;
1876
1877     case EL_DYNAMITE_ACTIVE:
1878     case EL_SP_DISK_RED_ACTIVE:
1879     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1880     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1881     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1882     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1883       MovDelay[x][y] = 96;
1884       break;
1885
1886     case EL_EM_DYNAMITE_ACTIVE:
1887       MovDelay[x][y] = 32;
1888       break;
1889
1890     case EL_LAMP:
1891       local_player->lights_still_needed++;
1892       break;
1893
1894     case EL_PENGUIN:
1895       local_player->friends_still_needed++;
1896       break;
1897
1898     case EL_PIG:
1899     case EL_DRAGON:
1900       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1901       break;
1902
1903     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1904     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1905     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1906     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1907     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1908     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1909     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1910     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1911     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1912     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1913     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1914     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1915       if (init_game)
1916       {
1917         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1918         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1919         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1920
1921         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1922         {
1923           game.belt_dir[belt_nr] = belt_dir;
1924           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1925         }
1926         else    /* more than one switch -- set it like the first switch */
1927         {
1928           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1929         }
1930       }
1931       break;
1932
1933 #if !USE_BOTH_SWITCHGATE_SWITCHES
1934     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1935       if (init_game)
1936         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1937       break;
1938
1939     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1940       if (init_game)
1941         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1942       break;
1943 #endif
1944
1945     case EL_LIGHT_SWITCH_ACTIVE:
1946       if (init_game)
1947         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1948       break;
1949
1950     case EL_INVISIBLE_STEELWALL:
1951     case EL_INVISIBLE_WALL:
1952     case EL_INVISIBLE_SAND:
1953       if (game.light_time_left > 0 ||
1954           game.lenses_time_left > 0)
1955         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1956       break;
1957
1958     case EL_EMC_MAGIC_BALL:
1959       if (game.ball_state)
1960         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1961       break;
1962
1963     case EL_EMC_MAGIC_BALL_SWITCH:
1964       if (game.ball_state)
1965         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1966       break;
1967
1968     case EL_TRIGGER_PLAYER:
1969     case EL_TRIGGER_ELEMENT:
1970     case EL_TRIGGER_CE_VALUE:
1971     case EL_TRIGGER_CE_SCORE:
1972     case EL_SELF:
1973     case EL_ANY_ELEMENT:
1974     case EL_CURRENT_CE_VALUE:
1975     case EL_CURRENT_CE_SCORE:
1976     case EL_PREV_CE_1:
1977     case EL_PREV_CE_2:
1978     case EL_PREV_CE_3:
1979     case EL_PREV_CE_4:
1980     case EL_PREV_CE_5:
1981     case EL_PREV_CE_6:
1982     case EL_PREV_CE_7:
1983     case EL_PREV_CE_8:
1984     case EL_NEXT_CE_1:
1985     case EL_NEXT_CE_2:
1986     case EL_NEXT_CE_3:
1987     case EL_NEXT_CE_4:
1988     case EL_NEXT_CE_5:
1989     case EL_NEXT_CE_6:
1990     case EL_NEXT_CE_7:
1991     case EL_NEXT_CE_8:
1992       /* reference elements should not be used on the playfield */
1993       Feld[x][y] = EL_EMPTY;
1994       break;
1995
1996     default:
1997       if (IS_CUSTOM_ELEMENT(element))
1998       {
1999         if (CAN_MOVE(element))
2000           InitMovDir(x, y);
2001
2002 #if USE_NEW_CUSTOM_VALUE
2003         if (!element_info[element].use_last_ce_value || init_game)
2004           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2005 #endif
2006       }
2007       else if (IS_GROUP_ELEMENT(element))
2008       {
2009         Feld[x][y] = GetElementFromGroupElement(element);
2010
2011         InitField(x, y, init_game);
2012       }
2013
2014       break;
2015   }
2016
2017   if (!init_game)
2018     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2019 }
2020
2021 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2022 {
2023   InitField(x, y, init_game);
2024
2025   /* not needed to call InitMovDir() -- already done by InitField()! */
2026   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2027       CAN_MOVE(Feld[x][y]))
2028     InitMovDir(x, y);
2029 }
2030
2031 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2032 {
2033   int old_element = Feld[x][y];
2034
2035   InitField(x, y, init_game);
2036
2037   /* not needed to call InitMovDir() -- already done by InitField()! */
2038   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2039       CAN_MOVE(old_element) &&
2040       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2041     InitMovDir(x, y);
2042
2043   /* this case is in fact a combination of not less than three bugs:
2044      first, it calls InitMovDir() for elements that can move, although this is
2045      already done by InitField(); then, it checks the element that was at this
2046      field _before_ the call to InitField() (which can change it); lastly, it
2047      was not called for "mole with direction" elements, which were treated as
2048      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2049   */
2050 }
2051
2052 #if 1
2053
2054 static int get_key_element_from_nr(int key_nr)
2055 {
2056   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2057                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2058                           EL_EM_KEY_1 : EL_KEY_1);
2059
2060   return key_base_element + key_nr;
2061 }
2062
2063 static int get_next_dropped_element(struct PlayerInfo *player)
2064 {
2065   return (player->inventory_size > 0 ?
2066           player->inventory_element[player->inventory_size - 1] :
2067           player->inventory_infinite_element != EL_UNDEFINED ?
2068           player->inventory_infinite_element :
2069           player->dynabombs_left > 0 ?
2070           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2071           EL_UNDEFINED);
2072 }
2073
2074 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2075 {
2076   /* pos >= 0: get element from bottom of the stack;
2077      pos <  0: get element from top of the stack */
2078
2079   if (pos < 0)
2080   {
2081     int min_inventory_size = -pos;
2082     int inventory_pos = player->inventory_size - min_inventory_size;
2083     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2084
2085     return (player->inventory_size >= min_inventory_size ?
2086             player->inventory_element[inventory_pos] :
2087             player->inventory_infinite_element != EL_UNDEFINED ?
2088             player->inventory_infinite_element :
2089             player->dynabombs_left >= min_dynabombs_left ?
2090             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2091             EL_UNDEFINED);
2092   }
2093   else
2094   {
2095     int min_dynabombs_left = pos + 1;
2096     int min_inventory_size = pos + 1 - player->dynabombs_left;
2097     int inventory_pos = pos - player->dynabombs_left;
2098
2099     return (player->inventory_infinite_element != EL_UNDEFINED ?
2100             player->inventory_infinite_element :
2101             player->dynabombs_left >= min_dynabombs_left ?
2102             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2103             player->inventory_size >= min_inventory_size ?
2104             player->inventory_element[inventory_pos] :
2105             EL_UNDEFINED);
2106   }
2107 }
2108
2109 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2110 {
2111   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2112   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2113   int compare_result;
2114
2115   if (gpo1->sort_priority != gpo2->sort_priority)
2116     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2117   else
2118     compare_result = gpo1->nr - gpo2->nr;
2119
2120   return compare_result;
2121 }
2122
2123 void InitGameControlValues()
2124 {
2125   int i;
2126
2127   for (i = 0; game_panel_controls[i].nr != -1; i++)
2128   {
2129     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2130     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2131     struct TextPosInfo *pos = gpc->pos;
2132     int nr = gpc->nr;
2133     int type = gpc->type;
2134
2135     if (nr != i)
2136     {
2137       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2138       Error(ERR_EXIT, "this should not happen -- please debug");
2139     }
2140
2141     /* force update of game controls after initialization */
2142     gpc->value = gpc->last_value = -1;
2143     gpc->frame = gpc->last_frame = -1;
2144     gpc->gfx_frame = -1;
2145
2146     /* determine panel value width for later calculation of alignment */
2147     if (type == TYPE_INTEGER || type == TYPE_STRING)
2148     {
2149       pos->width = pos->size * getFontWidth(pos->font);
2150       pos->height = getFontHeight(pos->font);
2151     }
2152     else if (type == TYPE_ELEMENT)
2153     {
2154       pos->width = pos->size;
2155       pos->height = pos->size;
2156     }
2157
2158     /* fill structure for game panel draw order */
2159     gpo->nr = gpc->nr;
2160     gpo->sort_priority = pos->sort_priority;
2161   }
2162
2163   /* sort game panel controls according to sort_priority and control number */
2164   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2165         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2166 }
2167
2168 void UpdatePlayfieldElementCount()
2169 {
2170   boolean use_element_count = FALSE;
2171   int i, j, x, y;
2172
2173   /* first check if it is needed at all to calculate playfield element count */
2174   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2175     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2176       use_element_count = TRUE;
2177
2178   if (!use_element_count)
2179     return;
2180
2181   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2182     element_info[i].element_count = 0;
2183
2184   SCAN_PLAYFIELD(x, y)
2185   {
2186     element_info[Feld[x][y]].element_count++;
2187   }
2188
2189   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2190     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2191       if (IS_IN_GROUP(j, i))
2192         element_info[EL_GROUP_START + i].element_count +=
2193           element_info[j].element_count;
2194 }
2195
2196 void UpdateGameControlValues()
2197 {
2198   int i, k;
2199   int time = (local_player->LevelSolved ?
2200               local_player->LevelSolved_CountingTime :
2201               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2202               level.native_em_level->lev->time :
2203               level.time == 0 ? TimePlayed : TimeLeft);
2204   int score = (local_player->LevelSolved ?
2205                local_player->LevelSolved_CountingScore :
2206                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207                level.native_em_level->lev->score :
2208                local_player->score);
2209   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2210               level.native_em_level->lev->required :
2211               local_player->gems_still_needed);
2212   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2213                      level.native_em_level->lev->required > 0 :
2214                      local_player->gems_still_needed > 0 ||
2215                      local_player->sokobanfields_still_needed > 0 ||
2216                      local_player->lights_still_needed > 0);
2217
2218   UpdatePlayfieldElementCount();
2219
2220   /* update game panel control values */
2221
2222   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2223   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2224
2225   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2226   for (i = 0; i < MAX_NUM_KEYS; i++)
2227     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2228   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2229   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2230
2231   if (game.centered_player_nr == -1)
2232   {
2233     for (i = 0; i < MAX_PLAYERS; i++)
2234     {
2235       for (k = 0; k < MAX_NUM_KEYS; k++)
2236       {
2237         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2238         {
2239           if (level.native_em_level->ply[i]->keys & (1 << k))
2240             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2241               get_key_element_from_nr(k);
2242         }
2243         else if (stored_player[i].key[k])
2244           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2245             get_key_element_from_nr(k);
2246       }
2247
2248       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2249         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2250           level.native_em_level->ply[i]->dynamite;
2251       else
2252         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2253           stored_player[i].inventory_size;
2254
2255       if (stored_player[i].num_white_keys > 0)
2256         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2257           EL_DC_KEY_WHITE;
2258
2259       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2260         stored_player[i].num_white_keys;
2261     }
2262   }
2263   else
2264   {
2265     int player_nr = game.centered_player_nr;
2266
2267     for (k = 0; k < MAX_NUM_KEYS; k++)
2268     {
2269       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2270       {
2271         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2272           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2273             get_key_element_from_nr(k);
2274       }
2275       else if (stored_player[player_nr].key[k])
2276         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2277           get_key_element_from_nr(k);
2278     }
2279
2280     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2281       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2282         level.native_em_level->ply[player_nr]->dynamite;
2283     else
2284       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285         stored_player[player_nr].inventory_size;
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   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2312     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2313      EL_EMPTY);
2314   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2315     local_player->shield_normal_time_left;
2316   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2317     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2318      EL_EMPTY);
2319   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2320     local_player->shield_deadly_time_left;
2321
2322   game_panel_controls[GAME_PANEL_EXIT].value =
2323     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2324
2325   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2326     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2327   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2328     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2329      EL_EMC_MAGIC_BALL_SWITCH);
2330
2331   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2332     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2333   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2334     game.light_time_left;
2335
2336   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2337     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2338   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2339     game.timegate_time_left;
2340
2341   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2342     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2343
2344   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2345     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2346   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2347     game.lenses_time_left;
2348
2349   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2350     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2351   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2352     game.magnify_time_left;
2353
2354   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2355     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2356      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2357      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2358      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2359      EL_BALLOON_SWITCH_NONE);
2360
2361   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2362     local_player->dynabomb_count;
2363   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2364     local_player->dynabomb_size;
2365   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2366     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2367
2368   game_panel_controls[GAME_PANEL_PENGUINS].value =
2369     local_player->friends_still_needed;
2370
2371   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2372     local_player->sokobanfields_still_needed;
2373   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2374     local_player->sokobanfields_still_needed;
2375
2376   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2377     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2378
2379   for (i = 0; i < NUM_BELTS; i++)
2380   {
2381     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2382       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2383        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2384     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2385       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2386   }
2387
2388   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2389     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2390   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2391     game.magic_wall_time_left;
2392
2393 #if USE_PLAYER_GRAVITY
2394   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2395     local_player->gravity;
2396 #else
2397   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2398 #endif
2399
2400   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2401     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2402
2403   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2404     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2405       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2406        game.panel.element[i].id : EL_UNDEFINED);
2407
2408   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2409     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2410       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2411        element_info[game.panel.element_count[i].id].element_count : 0);
2412
2413   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2414     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2415       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2416        element_info[game.panel.ce_score[i].id].collect_score : 0);
2417
2418   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2419     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2420       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2421        element_info[game.panel.ce_score_element[i].id].collect_score :
2422        EL_UNDEFINED);
2423
2424   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2425   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2426   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2427
2428   /* update game panel control frames */
2429
2430   for (i = 0; game_panel_controls[i].nr != -1; i++)
2431   {
2432     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2433
2434     if (gpc->type == TYPE_ELEMENT)
2435     {
2436       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2437       {
2438         int last_anim_random_frame = gfx.anim_random_frame;
2439         int element = gpc->value;
2440         int graphic = el2panelimg(element);
2441
2442         if (gpc->value != gpc->last_value)
2443         {
2444           gpc->gfx_frame = 0;
2445           gpc->gfx_random = INIT_GFX_RANDOM();
2446         }
2447         else
2448         {
2449           gpc->gfx_frame++;
2450
2451           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2452               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2453             gpc->gfx_random = INIT_GFX_RANDOM();
2454         }
2455
2456         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2457           gfx.anim_random_frame = gpc->gfx_random;
2458
2459         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2460           gpc->gfx_frame = element_info[element].collect_score;
2461
2462         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2463                                               gpc->gfx_frame);
2464
2465         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2466           gfx.anim_random_frame = last_anim_random_frame;
2467       }
2468     }
2469   }
2470 }
2471
2472 void DisplayGameControlValues()
2473 {
2474   boolean redraw_panel = FALSE;
2475   int i;
2476
2477   for (i = 0; game_panel_controls[i].nr != -1; i++)
2478   {
2479     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2480
2481     if (PANEL_DEACTIVATED(gpc->pos))
2482       continue;
2483
2484     if (gpc->value == gpc->last_value &&
2485         gpc->frame == gpc->last_frame)
2486       continue;
2487
2488     redraw_panel = TRUE;
2489   }
2490
2491   if (!redraw_panel)
2492     return;
2493
2494   /* copy default game door content to main double buffer */
2495   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2496              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2497
2498   /* redraw game control buttons */
2499 #if 1
2500   RedrawGameButtons();
2501 #else
2502   UnmapGameButtons();
2503   MapGameButtons();
2504 #endif
2505
2506   game_status = GAME_MODE_PSEUDO_PANEL;
2507
2508 #if 1
2509   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2510 #else
2511   for (i = 0; game_panel_controls[i].nr != -1; i++)
2512 #endif
2513   {
2514 #if 1
2515     int nr = game_panel_order[i].nr;
2516     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2517 #else
2518     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2519     int nr = gpc->nr;
2520 #endif
2521     struct TextPosInfo *pos = gpc->pos;
2522     int type = gpc->type;
2523     int value = gpc->value;
2524     int frame = gpc->frame;
2525 #if 0
2526     int last_value = gpc->last_value;
2527     int last_frame = gpc->last_frame;
2528 #endif
2529     int size = pos->size;
2530     int font = pos->font;
2531     boolean draw_masked = pos->draw_masked;
2532     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2533
2534     if (PANEL_DEACTIVATED(pos))
2535       continue;
2536
2537 #if 0
2538     if (value == last_value && frame == last_frame)
2539       continue;
2540 #endif
2541
2542     gpc->last_value = value;
2543     gpc->last_frame = frame;
2544
2545 #if 0
2546     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2547 #endif
2548
2549     if (type == TYPE_INTEGER)
2550     {
2551       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2552           nr == GAME_PANEL_TIME)
2553       {
2554         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2555
2556         if (use_dynamic_size)           /* use dynamic number of digits */
2557         {
2558           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2559           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2560           int size2 = size1 + 1;
2561           int font1 = pos->font;
2562           int font2 = pos->font_alt;
2563
2564           size = (value < value_change ? size1 : size2);
2565           font = (value < value_change ? font1 : font2);
2566
2567 #if 0
2568           /* clear background if value just changed its size (dynamic digits) */
2569           if ((last_value < value_change) != (value < value_change))
2570           {
2571             int width1 = size1 * getFontWidth(font1);
2572             int width2 = size2 * getFontWidth(font2);
2573             int max_width = MAX(width1, width2);
2574             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2575
2576             pos->width = max_width;
2577
2578             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2579                                        max_width, max_height);
2580           }
2581 #endif
2582         }
2583       }
2584
2585 #if 1
2586       /* correct text size if "digits" is zero or less */
2587       if (size <= 0)
2588         size = strlen(int2str(value, size));
2589
2590       /* dynamically correct text alignment */
2591       pos->width = size * getFontWidth(font);
2592 #endif
2593
2594       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2595                   int2str(value, size), font, mask_mode);
2596     }
2597     else if (type == TYPE_ELEMENT)
2598     {
2599       int element, graphic;
2600       Bitmap *src_bitmap;
2601       int src_x, src_y;
2602       int width, height;
2603       int dst_x = PANEL_XPOS(pos);
2604       int dst_y = PANEL_YPOS(pos);
2605
2606 #if 1
2607       if (value != EL_UNDEFINED && value != EL_EMPTY)
2608       {
2609         element = value;
2610         graphic = el2panelimg(value);
2611
2612         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2613
2614 #if 1
2615         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2616           size = TILESIZE;
2617 #endif
2618
2619         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2620                               &src_x, &src_y);
2621
2622         width  = graphic_info[graphic].width  * size / TILESIZE;
2623         height = graphic_info[graphic].height * size / TILESIZE;
2624
2625         if (draw_masked)
2626         {
2627           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2628                         dst_x - src_x, dst_y - src_y);
2629           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2630                            dst_x, dst_y);
2631         }
2632         else
2633         {
2634           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2635                      dst_x, dst_y);
2636         }
2637       }
2638 #else
2639       if (value == EL_UNDEFINED || value == EL_EMPTY)
2640       {
2641         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2642         graphic = el2panelimg(element);
2643
2644         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2645         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2646         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2647       }
2648       else
2649       {
2650         element = value;
2651         graphic = el2panelimg(value);
2652
2653         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2654       }
2655
2656       width  = graphic_info[graphic].width  * size / TILESIZE;
2657       height = graphic_info[graphic].height * size / TILESIZE;
2658
2659       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2660 #endif
2661     }
2662     else if (type == TYPE_STRING)
2663     {
2664       boolean active = (value != 0);
2665       char *state_normal = "off";
2666       char *state_active = "on";
2667       char *state = (active ? state_active : state_normal);
2668       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2669                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2670                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2671                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2672
2673       if (nr == GAME_PANEL_GRAVITY_STATE)
2674       {
2675         int font1 = pos->font;          /* (used for normal state) */
2676         int font2 = pos->font_alt;      /* (used for active state) */
2677 #if 0
2678         int size1 = strlen(state_normal);
2679         int size2 = strlen(state_active);
2680         int width1 = size1 * getFontWidth(font1);
2681         int width2 = size2 * getFontWidth(font2);
2682         int max_width = MAX(width1, width2);
2683         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2684
2685         pos->width = max_width;
2686
2687         /* clear background for values that may have changed its size */
2688         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2689                                    max_width, max_height);
2690 #endif
2691
2692         font = (active ? font2 : font1);
2693       }
2694
2695       if (s != NULL)
2696       {
2697         char *s_cut;
2698
2699 #if 1
2700         if (size <= 0)
2701         {
2702           /* don't truncate output if "chars" is zero or less */
2703           size = strlen(s);
2704
2705           /* dynamically correct text alignment */
2706           pos->width = size * getFontWidth(font);
2707         }
2708 #endif
2709
2710         s_cut = getStringCopyN(s, size);
2711
2712         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2713                     s_cut, font, mask_mode);
2714
2715         free(s_cut);
2716       }
2717     }
2718
2719     redraw_mask |= REDRAW_DOOR_1;
2720   }
2721
2722   game_status = GAME_MODE_PLAYING;
2723 }
2724
2725 void UpdateAndDisplayGameControlValues()
2726 {
2727   if (tape.warp_forward)
2728     return;
2729
2730   UpdateGameControlValues();
2731   DisplayGameControlValues();
2732 }
2733
2734 void DrawGameValue_Emeralds(int value)
2735 {
2736   struct TextPosInfo *pos = &game.panel.gems;
2737 #if 1
2738   int font_nr = pos->font;
2739 #else
2740   int font_nr = FONT_TEXT_2;
2741 #endif
2742   int font_width = getFontWidth(font_nr);
2743   int chars = pos->size;
2744
2745 #if 1
2746   return;       /* !!! USE NEW STUFF !!! */
2747 #endif
2748
2749   if (PANEL_DEACTIVATED(pos))
2750     return;
2751
2752   pos->width = chars * font_width;
2753
2754   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2755 }
2756
2757 void DrawGameValue_Dynamite(int value)
2758 {
2759   struct TextPosInfo *pos = &game.panel.inventory_count;
2760 #if 1
2761   int font_nr = pos->font;
2762 #else
2763   int font_nr = FONT_TEXT_2;
2764 #endif
2765   int font_width = getFontWidth(font_nr);
2766   int chars = pos->size;
2767
2768 #if 1
2769   return;       /* !!! USE NEW STUFF !!! */
2770 #endif
2771
2772   if (PANEL_DEACTIVATED(pos))
2773     return;
2774
2775   pos->width = chars * font_width;
2776
2777   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2778 }
2779
2780 void DrawGameValue_Score(int value)
2781 {
2782   struct TextPosInfo *pos = &game.panel.score;
2783 #if 1
2784   int font_nr = pos->font;
2785 #else
2786   int font_nr = FONT_TEXT_2;
2787 #endif
2788   int font_width = getFontWidth(font_nr);
2789   int chars = pos->size;
2790
2791 #if 1
2792   return;       /* !!! USE NEW STUFF !!! */
2793 #endif
2794
2795   if (PANEL_DEACTIVATED(pos))
2796     return;
2797
2798   pos->width = chars * font_width;
2799
2800   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2801 }
2802
2803 void DrawGameValue_Time(int value)
2804 {
2805   struct TextPosInfo *pos = &game.panel.time;
2806   static int last_value = -1;
2807   int chars1 = 3;
2808   int chars2 = 4;
2809   int chars = pos->size;
2810 #if 1
2811   int font1_nr = pos->font;
2812   int font2_nr = pos->font_alt;
2813 #else
2814   int font1_nr = FONT_TEXT_2;
2815   int font2_nr = FONT_TEXT_1;
2816 #endif
2817   int font_nr = font1_nr;
2818   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2819
2820 #if 1
2821   return;       /* !!! USE NEW STUFF !!! */
2822 #endif
2823
2824   if (PANEL_DEACTIVATED(pos))
2825     return;
2826
2827   if (use_dynamic_chars)                /* use dynamic number of chars */
2828   {
2829     chars   = (value < 1000 ? chars1   : chars2);
2830     font_nr = (value < 1000 ? font1_nr : font2_nr);
2831   }
2832
2833   /* clear background if value just changed its size (dynamic chars only) */
2834   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2835   {
2836     int width1 = chars1 * getFontWidth(font1_nr);
2837     int width2 = chars2 * getFontWidth(font2_nr);
2838     int max_width = MAX(width1, width2);
2839     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2840
2841     pos->width = max_width;
2842
2843     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2844                                max_width, max_height);
2845   }
2846
2847   pos->width = chars * getFontWidth(font_nr);
2848
2849   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2850
2851   last_value = value;
2852 }
2853
2854 void DrawGameValue_Level(int value)
2855 {
2856   struct TextPosInfo *pos = &game.panel.level_number;
2857   int chars1 = 2;
2858   int chars2 = 3;
2859   int chars = pos->size;
2860 #if 1
2861   int font1_nr = pos->font;
2862   int font2_nr = pos->font_alt;
2863 #else
2864   int font1_nr = FONT_TEXT_2;
2865   int font2_nr = FONT_TEXT_1;
2866 #endif
2867   int font_nr = font1_nr;
2868   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2869
2870 #if 1
2871   return;       /* !!! USE NEW STUFF !!! */
2872 #endif
2873
2874   if (PANEL_DEACTIVATED(pos))
2875     return;
2876
2877   if (use_dynamic_chars)                /* use dynamic number of chars */
2878   {
2879     chars   = (level_nr < 100 ? chars1   : chars2);
2880     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2881   }
2882
2883   pos->width = chars * getFontWidth(font_nr);
2884
2885   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2886 }
2887
2888 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2889 {
2890 #if 0
2891   struct TextPosInfo *pos = &game.panel.keys;
2892 #endif
2893 #if 0
2894   int base_key_graphic = EL_KEY_1;
2895 #endif
2896   int i;
2897
2898 #if 1
2899   return;       /* !!! USE NEW STUFF !!! */
2900 #endif
2901
2902 #if 0
2903   if (PANEL_DEACTIVATED(pos))
2904     return;
2905 #endif
2906
2907 #if 0
2908   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2909     base_key_graphic = EL_EM_KEY_1;
2910 #endif
2911
2912 #if 0
2913   pos->width = 4 * MINI_TILEX;
2914 #endif
2915
2916 #if 1
2917   for (i = 0; i < MAX_NUM_KEYS; i++)
2918 #else
2919   /* currently only 4 of 8 possible keys are displayed */
2920   for (i = 0; i < STD_NUM_KEYS; i++)
2921 #endif
2922   {
2923 #if 1
2924     struct TextPosInfo *pos = &game.panel.key[i];
2925 #endif
2926     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2927     int src_y = DOOR_GFX_PAGEY1 + 123;
2928 #if 1
2929     int dst_x = PANEL_XPOS(pos);
2930     int dst_y = PANEL_YPOS(pos);
2931 #else
2932     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2933     int dst_y = PANEL_YPOS(pos);
2934 #endif
2935
2936 #if 1
2937     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2938                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2939                    EL_KEY_1) + i;
2940     int graphic = el2edimg(element);
2941 #endif
2942
2943 #if 1
2944     if (PANEL_DEACTIVATED(pos))
2945       continue;
2946 #endif
2947
2948 #if 0
2949     /* masked blit with tiles from half-size scaled bitmap does not work yet
2950        (no mask bitmap created for these sizes after loading and scaling) --
2951        solution: load without creating mask, scale, then create final mask */
2952
2953     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2954                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2955
2956     if (key[i])
2957     {
2958 #if 0
2959       int graphic = el2edimg(base_key_graphic + i);
2960 #endif
2961       Bitmap *src_bitmap;
2962       int src_x, src_y;
2963
2964       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2965
2966       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2967                     dst_x - src_x, dst_y - src_y);
2968       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2969                        dst_x, dst_y);
2970     }
2971 #else
2972 #if 1
2973     if (key[i])
2974       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2975     else
2976       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2977                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2978 #else
2979     if (key[i])
2980       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2981     else
2982       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2983                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2984 #endif
2985 #endif
2986   }
2987 }
2988
2989 #else
2990
2991 void DrawGameValue_Emeralds(int value)
2992 {
2993   int font_nr = FONT_TEXT_2;
2994   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2995
2996   if (PANEL_DEACTIVATED(game.panel.gems))
2997     return;
2998
2999   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3000 }
3001
3002 void DrawGameValue_Dynamite(int value)
3003 {
3004   int font_nr = FONT_TEXT_2;
3005   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3006
3007   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3008     return;
3009
3010   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3011 }
3012
3013 void DrawGameValue_Score(int value)
3014 {
3015   int font_nr = FONT_TEXT_2;
3016   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3017
3018   if (PANEL_DEACTIVATED(game.panel.score))
3019     return;
3020
3021   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3022 }
3023
3024 void DrawGameValue_Time(int value)
3025 {
3026   int font1_nr = FONT_TEXT_2;
3027 #if 1
3028   int font2_nr = FONT_TEXT_1;
3029 #else
3030   int font2_nr = FONT_LEVEL_NUMBER;
3031 #endif
3032   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3033   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3034
3035   if (PANEL_DEACTIVATED(game.panel.time))
3036     return;
3037
3038   /* clear background if value just changed its size */
3039   if (value == 999 || value == 1000)
3040     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3041
3042   if (value < 1000)
3043     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3044   else
3045     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3046 }
3047
3048 void DrawGameValue_Level(int value)
3049 {
3050   int font1_nr = FONT_TEXT_2;
3051 #if 1
3052   int font2_nr = FONT_TEXT_1;
3053 #else
3054   int font2_nr = FONT_LEVEL_NUMBER;
3055 #endif
3056
3057   if (PANEL_DEACTIVATED(game.panel.level))
3058     return;
3059
3060   if (level_nr < 100)
3061     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3062   else
3063     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3064 }
3065
3066 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3067 {
3068   int base_key_graphic = EL_KEY_1;
3069   int i;
3070
3071   if (PANEL_DEACTIVATED(game.panel.keys))
3072     return;
3073
3074   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3075     base_key_graphic = EL_EM_KEY_1;
3076
3077   /* currently only 4 of 8 possible keys are displayed */
3078   for (i = 0; i < STD_NUM_KEYS; i++)
3079   {
3080     int x = XX_KEYS + i * MINI_TILEX;
3081     int y = YY_KEYS;
3082
3083     if (key[i])
3084       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3085     else
3086       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3087                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3088   }
3089 }
3090
3091 #endif
3092
3093 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3094                        int key_bits)
3095 {
3096   int key[MAX_NUM_KEYS];
3097   int i;
3098
3099   /* prevent EM engine from updating time/score values parallel to GameWon() */
3100   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3101       local_player->LevelSolved)
3102     return;
3103
3104   for (i = 0; i < MAX_NUM_KEYS; i++)
3105     key[i] = key_bits & (1 << i);
3106
3107   DrawGameValue_Level(level_nr);
3108
3109   DrawGameValue_Emeralds(emeralds);
3110   DrawGameValue_Dynamite(dynamite);
3111   DrawGameValue_Score(score);
3112   DrawGameValue_Time(time);
3113
3114   DrawGameValue_Keys(key);
3115 }
3116
3117 void UpdateGameDoorValues()
3118 {
3119   UpdateGameControlValues();
3120 }
3121
3122 void DrawGameDoorValues()
3123 {
3124   DisplayGameControlValues();
3125 }
3126
3127 void DrawGameDoorValues_OLD()
3128 {
3129   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3130   int dynamite_value = 0;
3131   int score_value = (local_player->LevelSolved ? local_player->score_final :
3132                      local_player->score);
3133   int gems_value = local_player->gems_still_needed;
3134   int key_bits = 0;
3135   int i, j;
3136
3137   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3138   {
3139     DrawGameDoorValues_EM();
3140
3141     return;
3142   }
3143
3144   if (game.centered_player_nr == -1)
3145   {
3146     for (i = 0; i < MAX_PLAYERS; i++)
3147     {
3148       for (j = 0; j < MAX_NUM_KEYS; j++)
3149         if (stored_player[i].key[j])
3150           key_bits |= (1 << j);
3151
3152       dynamite_value += stored_player[i].inventory_size;
3153     }
3154   }
3155   else
3156   {
3157     int player_nr = game.centered_player_nr;
3158
3159     for (i = 0; i < MAX_NUM_KEYS; i++)
3160       if (stored_player[player_nr].key[i])
3161         key_bits |= (1 << i);
3162
3163     dynamite_value = stored_player[player_nr].inventory_size;
3164   }
3165
3166   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3167                     key_bits);
3168 }
3169
3170
3171 /*
3172   =============================================================================
3173   InitGameEngine()
3174   -----------------------------------------------------------------------------
3175   initialize game engine due to level / tape version number
3176   =============================================================================
3177 */
3178
3179 static void InitGameEngine()
3180 {
3181   int i, j, k, l, x, y;
3182
3183   /* set game engine from tape file when re-playing, else from level file */
3184   game.engine_version = (tape.playing ? tape.engine_version :
3185                          level.game_version);
3186
3187   /* ---------------------------------------------------------------------- */
3188   /* set flags for bugs and changes according to active game engine version */
3189   /* ---------------------------------------------------------------------- */
3190
3191   /*
3192     Summary of bugfix/change:
3193     Fixed handling for custom elements that change when pushed by the player.
3194
3195     Fixed/changed in version:
3196     3.1.0
3197
3198     Description:
3199     Before 3.1.0, custom elements that "change when pushing" changed directly
3200     after the player started pushing them (until then handled in "DigField()").
3201     Since 3.1.0, these custom elements are not changed until the "pushing"
3202     move of the element is finished (now handled in "ContinueMoving()").
3203
3204     Affected levels/tapes:
3205     The first condition is generally needed for all levels/tapes before version
3206     3.1.0, which might use the old behaviour before it was changed; known tapes
3207     that are affected are some tapes from the level set "Walpurgis Gardens" by
3208     Jamie Cullen.
3209     The second condition is an exception from the above case and is needed for
3210     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3211     above (including some development versions of 3.1.0), but before it was
3212     known that this change would break tapes like the above and was fixed in
3213     3.1.1, so that the changed behaviour was active although the engine version
3214     while recording maybe was before 3.1.0. There is at least one tape that is
3215     affected by this exception, which is the tape for the one-level set "Bug
3216     Machine" by Juergen Bonhagen.
3217   */
3218
3219   game.use_change_when_pushing_bug =
3220     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3221      !(tape.playing &&
3222        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3223        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3224
3225   /*
3226     Summary of bugfix/change:
3227     Fixed handling for blocking the field the player leaves when moving.
3228
3229     Fixed/changed in version:
3230     3.1.1
3231
3232     Description:
3233     Before 3.1.1, when "block last field when moving" was enabled, the field
3234     the player is leaving when moving was blocked for the time of the move,
3235     and was directly unblocked afterwards. This resulted in the last field
3236     being blocked for exactly one less than the number of frames of one player
3237     move. Additionally, even when blocking was disabled, the last field was
3238     blocked for exactly one frame.
3239     Since 3.1.1, due to changes in player movement handling, the last field
3240     is not blocked at all when blocking is disabled. When blocking is enabled,
3241     the last field is blocked for exactly the number of frames of one player
3242     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3243     last field is blocked for exactly one more than the number of frames of
3244     one player move.
3245
3246     Affected levels/tapes:
3247     (!!! yet to be determined -- probably many !!!)
3248   */
3249
3250   game.use_block_last_field_bug =
3251     (game.engine_version < VERSION_IDENT(3,1,1,0));
3252
3253   /*
3254     Summary of bugfix/change:
3255     Changed behaviour of CE changes with multiple changes per single frame.
3256
3257     Fixed/changed in version:
3258     3.2.0-6
3259
3260     Description:
3261     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3262     This resulted in race conditions where CEs seem to behave strange in some
3263     situations (where triggered CE changes were just skipped because there was
3264     already a CE change on that tile in the playfield in that engine frame).
3265     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3266     (The number of changes per frame must be limited in any case, because else
3267     it is easily possible to define CE changes that would result in an infinite
3268     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3269     should be set large enough so that it would only be reached in cases where
3270     the corresponding CE change conditions run into a loop. Therefore, it seems
3271     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3272     maximal number of change pages for custom elements.)
3273
3274     Affected levels/tapes:
3275     Probably many.
3276   */
3277
3278 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3279   game.max_num_changes_per_frame = 1;
3280 #else
3281   game.max_num_changes_per_frame =
3282     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3283 #endif
3284
3285   /* ---------------------------------------------------------------------- */
3286
3287   /* default scan direction: scan playfield from top/left to bottom/right */
3288   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3289
3290   /* dynamically adjust element properties according to game engine version */
3291   InitElementPropertiesEngine(game.engine_version);
3292
3293 #if 0
3294   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3295   printf("          tape version == %06d [%s] [file: %06d]\n",
3296          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3297          tape.file_version);
3298   printf("       => game.engine_version == %06d\n", game.engine_version);
3299 #endif
3300
3301   /* ---------- initialize player's initial move delay --------------------- */
3302
3303   /* dynamically adjust player properties according to level information */
3304   for (i = 0; i < MAX_PLAYERS; i++)
3305     game.initial_move_delay_value[i] =
3306       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3307
3308   /* dynamically adjust player properties according to game engine version */
3309   for (i = 0; i < MAX_PLAYERS; i++)
3310     game.initial_move_delay[i] =
3311       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3312        game.initial_move_delay_value[i] : 0);
3313
3314   /* ---------- initialize player's initial push delay --------------------- */
3315
3316   /* dynamically adjust player properties according to game engine version */
3317   game.initial_push_delay_value =
3318     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3319
3320   /* ---------- initialize changing elements ------------------------------- */
3321
3322   /* initialize changing elements information */
3323   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3324   {
3325     struct ElementInfo *ei = &element_info[i];
3326
3327     /* this pointer might have been changed in the level editor */
3328     ei->change = &ei->change_page[0];
3329
3330     if (!IS_CUSTOM_ELEMENT(i))
3331     {
3332       ei->change->target_element = EL_EMPTY_SPACE;
3333       ei->change->delay_fixed = 0;
3334       ei->change->delay_random = 0;
3335       ei->change->delay_frames = 1;
3336     }
3337
3338     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3339     {
3340       ei->has_change_event[j] = FALSE;
3341
3342       ei->event_page_nr[j] = 0;
3343       ei->event_page[j] = &ei->change_page[0];
3344     }
3345   }
3346
3347   /* add changing elements from pre-defined list */
3348   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3349   {
3350     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3351     struct ElementInfo *ei = &element_info[ch_delay->element];
3352
3353     ei->change->target_element       = ch_delay->target_element;
3354     ei->change->delay_fixed          = ch_delay->change_delay;
3355
3356     ei->change->pre_change_function  = ch_delay->pre_change_function;
3357     ei->change->change_function      = ch_delay->change_function;
3358     ei->change->post_change_function = ch_delay->post_change_function;
3359
3360     ei->change->can_change = TRUE;
3361     ei->change->can_change_or_has_action = TRUE;
3362
3363     ei->has_change_event[CE_DELAY] = TRUE;
3364
3365     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3366     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3367   }
3368
3369   /* ---------- initialize internal run-time variables --------------------- */
3370
3371   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3372   {
3373     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3374
3375     for (j = 0; j < ei->num_change_pages; j++)
3376     {
3377       ei->change_page[j].can_change_or_has_action =
3378         (ei->change_page[j].can_change |
3379          ei->change_page[j].has_action);
3380     }
3381   }
3382
3383   /* add change events from custom element configuration */
3384   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3385   {
3386     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3387
3388     for (j = 0; j < ei->num_change_pages; j++)
3389     {
3390       if (!ei->change_page[j].can_change_or_has_action)
3391         continue;
3392
3393       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3394       {
3395         /* only add event page for the first page found with this event */
3396         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3397         {
3398           ei->has_change_event[k] = TRUE;
3399
3400           ei->event_page_nr[k] = j;
3401           ei->event_page[k] = &ei->change_page[j];
3402         }
3403       }
3404     }
3405   }
3406
3407 #if 1
3408   /* ---------- initialize reference elements in change conditions --------- */
3409
3410   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3411   {
3412     int element = EL_CUSTOM_START + i;
3413     struct ElementInfo *ei = &element_info[element];
3414
3415     for (j = 0; j < ei->num_change_pages; j++)
3416     {
3417       int trigger_element = ei->change_page[j].initial_trigger_element;
3418
3419       if (trigger_element >= EL_PREV_CE_8 &&
3420           trigger_element <= EL_NEXT_CE_8)
3421         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3422
3423       ei->change_page[j].trigger_element = trigger_element;
3424     }
3425   }
3426 #endif
3427
3428   /* ---------- initialize run-time trigger player and element ------------- */
3429
3430   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3431   {
3432     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3433
3434     for (j = 0; j < ei->num_change_pages; j++)
3435     {
3436       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3437       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3438       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3439       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3440       ei->change_page[j].actual_trigger_ce_value = 0;
3441       ei->change_page[j].actual_trigger_ce_score = 0;
3442     }
3443   }
3444
3445   /* ---------- initialize trigger events ---------------------------------- */
3446
3447   /* initialize trigger events information */
3448   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3449     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3450       trigger_events[i][j] = FALSE;
3451
3452   /* add trigger events from element change event properties */
3453   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3454   {
3455     struct ElementInfo *ei = &element_info[i];
3456
3457     for (j = 0; j < ei->num_change_pages; j++)
3458     {
3459       if (!ei->change_page[j].can_change_or_has_action)
3460         continue;
3461
3462       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3463       {
3464         int trigger_element = ei->change_page[j].trigger_element;
3465
3466         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3467         {
3468           if (ei->change_page[j].has_event[k])
3469           {
3470             if (IS_GROUP_ELEMENT(trigger_element))
3471             {
3472               struct ElementGroupInfo *group =
3473                 element_info[trigger_element].group;
3474
3475               for (l = 0; l < group->num_elements_resolved; l++)
3476                 trigger_events[group->element_resolved[l]][k] = TRUE;
3477             }
3478             else if (trigger_element == EL_ANY_ELEMENT)
3479               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3480                 trigger_events[l][k] = TRUE;
3481             else
3482               trigger_events[trigger_element][k] = TRUE;
3483           }
3484         }
3485       }
3486     }
3487   }
3488
3489   /* ---------- initialize push delay -------------------------------------- */
3490
3491   /* initialize push delay values to default */
3492   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3493   {
3494     if (!IS_CUSTOM_ELEMENT(i))
3495     {
3496       /* set default push delay values (corrected since version 3.0.7-1) */
3497       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3498       {
3499         element_info[i].push_delay_fixed = 2;
3500         element_info[i].push_delay_random = 8;
3501       }
3502       else
3503       {
3504         element_info[i].push_delay_fixed = 8;
3505         element_info[i].push_delay_random = 8;
3506       }
3507     }
3508   }
3509
3510   /* set push delay value for certain elements from pre-defined list */
3511   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3512   {
3513     int e = push_delay_list[i].element;
3514
3515     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3516     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3517   }
3518
3519   /* set push delay value for Supaplex elements for newer engine versions */
3520   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3521   {
3522     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3523     {
3524       if (IS_SP_ELEMENT(i))
3525       {
3526         /* set SP push delay to just enough to push under a falling zonk */
3527         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3528
3529         element_info[i].push_delay_fixed  = delay;
3530         element_info[i].push_delay_random = 0;
3531       }
3532     }
3533   }
3534
3535   /* ---------- initialize move stepsize ----------------------------------- */
3536
3537   /* initialize move stepsize values to default */
3538   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3539     if (!IS_CUSTOM_ELEMENT(i))
3540       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3541
3542   /* set move stepsize value for certain elements from pre-defined list */
3543   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3544   {
3545     int e = move_stepsize_list[i].element;
3546
3547     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3548   }
3549
3550   /* ---------- initialize collect score ----------------------------------- */
3551
3552   /* initialize collect score values for custom elements from initial value */
3553   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3554     if (IS_CUSTOM_ELEMENT(i))
3555       element_info[i].collect_score = element_info[i].collect_score_initial;
3556
3557   /* ---------- initialize collect count ----------------------------------- */
3558
3559   /* initialize collect count values for non-custom elements */
3560   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3561     if (!IS_CUSTOM_ELEMENT(i))
3562       element_info[i].collect_count_initial = 0;
3563
3564   /* add collect count values for all elements from pre-defined list */
3565   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3566     element_info[collect_count_list[i].element].collect_count_initial =
3567       collect_count_list[i].count;
3568
3569   /* ---------- initialize access direction -------------------------------- */
3570
3571   /* initialize access direction values to default (access from every side) */
3572   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3573     if (!IS_CUSTOM_ELEMENT(i))
3574       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3575
3576   /* set access direction value for certain elements from pre-defined list */
3577   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3578     element_info[access_direction_list[i].element].access_direction =
3579       access_direction_list[i].direction;
3580
3581   /* ---------- initialize explosion content ------------------------------- */
3582   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3583   {
3584     if (IS_CUSTOM_ELEMENT(i))
3585       continue;
3586
3587     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3588     {
3589       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3590
3591       element_info[i].content.e[x][y] =
3592         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3593          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3594          i == EL_PLAYER_3 ? EL_EMERALD :
3595          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3596          i == EL_MOLE ? EL_EMERALD_RED :
3597          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3598          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3599          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3600          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3601          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3602          i == EL_WALL_EMERALD ? EL_EMERALD :
3603          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3604          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3605          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3606          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3607          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3608          i == EL_WALL_PEARL ? EL_PEARL :
3609          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3610          EL_EMPTY);
3611     }
3612   }
3613
3614   /* ---------- initialize recursion detection ------------------------------ */
3615   recursion_loop_depth = 0;
3616   recursion_loop_detected = FALSE;
3617   recursion_loop_element = EL_UNDEFINED;
3618
3619   /* ---------- initialize graphics engine ---------------------------------- */
3620   game.scroll_delay_value =
3621     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3622      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3623   game.scroll_delay_value =
3624     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3625 }
3626
3627 int get_num_special_action(int element, int action_first, int action_last)
3628 {
3629   int num_special_action = 0;
3630   int i, j;
3631
3632   for (i = action_first; i <= action_last; i++)
3633   {
3634     boolean found = FALSE;
3635
3636     for (j = 0; j < NUM_DIRECTIONS; j++)
3637       if (el_act_dir2img(element, i, j) !=
3638           el_act_dir2img(element, ACTION_DEFAULT, j))
3639         found = TRUE;
3640
3641     if (found)
3642       num_special_action++;
3643     else
3644       break;
3645   }
3646
3647   return num_special_action;
3648 }
3649
3650
3651 /*
3652   =============================================================================
3653   InitGame()
3654   -----------------------------------------------------------------------------
3655   initialize and start new game
3656   =============================================================================
3657 */
3658
3659 void InitGame()
3660 {
3661   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3662   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3663   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3664 #if 0
3665   boolean do_fading = (game_status == GAME_MODE_MAIN);
3666 #endif
3667   int i, j, x, y;
3668
3669   game_status = GAME_MODE_PLAYING;
3670
3671   InitGameEngine();
3672   InitGameControlValues();
3673
3674   /* don't play tapes over network */
3675   network_playing = (options.network && !tape.playing);
3676
3677   for (i = 0; i < MAX_PLAYERS; i++)
3678   {
3679     struct PlayerInfo *player = &stored_player[i];
3680
3681     player->index_nr = i;
3682     player->index_bit = (1 << i);
3683     player->element_nr = EL_PLAYER_1 + i;
3684
3685     player->present = FALSE;
3686     player->active = FALSE;
3687     player->killed = FALSE;
3688
3689     player->action = 0;
3690     player->effective_action = 0;
3691     player->programmed_action = 0;
3692
3693     player->score = 0;
3694     player->score_final = 0;
3695
3696     player->gems_still_needed = level.gems_needed;
3697     player->sokobanfields_still_needed = 0;
3698     player->lights_still_needed = 0;
3699     player->friends_still_needed = 0;
3700
3701     for (j = 0; j < MAX_NUM_KEYS; j++)
3702       player->key[j] = FALSE;
3703
3704     player->num_white_keys = 0;
3705
3706     player->dynabomb_count = 0;
3707     player->dynabomb_size = 1;
3708     player->dynabombs_left = 0;
3709     player->dynabomb_xl = FALSE;
3710
3711     player->MovDir = MV_NONE;
3712     player->MovPos = 0;
3713     player->GfxPos = 0;
3714     player->GfxDir = MV_NONE;
3715     player->GfxAction = ACTION_DEFAULT;
3716     player->Frame = 0;
3717     player->StepFrame = 0;
3718
3719     player->use_murphy = FALSE;
3720     player->artwork_element =
3721       (level.use_artwork_element[i] ? level.artwork_element[i] :
3722        player->element_nr);
3723
3724     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3725     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3726
3727     player->gravity = level.initial_player_gravity[i];
3728
3729     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3730
3731     player->actual_frame_counter = 0;
3732
3733     player->step_counter = 0;
3734
3735     player->last_move_dir = MV_NONE;
3736
3737     player->is_active = FALSE;
3738
3739     player->is_waiting = FALSE;
3740     player->is_moving = FALSE;
3741     player->is_auto_moving = FALSE;
3742     player->is_digging = FALSE;
3743     player->is_snapping = FALSE;
3744     player->is_collecting = FALSE;
3745     player->is_pushing = FALSE;
3746     player->is_switching = FALSE;
3747     player->is_dropping = FALSE;
3748     player->is_dropping_pressed = FALSE;
3749
3750     player->is_bored = FALSE;
3751     player->is_sleeping = FALSE;
3752
3753     player->frame_counter_bored = -1;
3754     player->frame_counter_sleeping = -1;
3755
3756     player->anim_delay_counter = 0;
3757     player->post_delay_counter = 0;
3758
3759     player->dir_waiting = MV_NONE;
3760     player->action_waiting = ACTION_DEFAULT;
3761     player->last_action_waiting = ACTION_DEFAULT;
3762     player->special_action_bored = ACTION_DEFAULT;
3763     player->special_action_sleeping = ACTION_DEFAULT;
3764
3765     player->switch_x = -1;
3766     player->switch_y = -1;
3767
3768     player->drop_x = -1;
3769     player->drop_y = -1;
3770
3771     player->show_envelope = 0;
3772
3773     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3774
3775     player->push_delay       = -1;      /* initialized when pushing starts */
3776     player->push_delay_value = game.initial_push_delay_value;
3777
3778     player->drop_delay = 0;
3779     player->drop_pressed_delay = 0;
3780
3781     player->last_jx = -1;
3782     player->last_jy = -1;
3783     player->jx = -1;
3784     player->jy = -1;
3785
3786     player->shield_normal_time_left = 0;
3787     player->shield_deadly_time_left = 0;
3788
3789     player->inventory_infinite_element = EL_UNDEFINED;
3790     player->inventory_size = 0;
3791
3792     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3793     SnapField(player, 0, 0);
3794
3795     player->LevelSolved = FALSE;
3796     player->GameOver = FALSE;
3797
3798     player->LevelSolved_GameWon = FALSE;
3799     player->LevelSolved_GameEnd = FALSE;
3800     player->LevelSolved_PanelOff = FALSE;
3801     player->LevelSolved_SaveTape = FALSE;
3802     player->LevelSolved_SaveScore = FALSE;
3803     player->LevelSolved_CountingTime = 0;
3804     player->LevelSolved_CountingScore = 0;
3805   }
3806
3807   network_player_action_received = FALSE;
3808
3809 #if defined(NETWORK_AVALIABLE)
3810   /* initial null action */
3811   if (network_playing)
3812     SendToServer_MovePlayer(MV_NONE);
3813 #endif
3814
3815   ZX = ZY = -1;
3816   ExitX = ExitY = -1;
3817
3818   FrameCounter = 0;
3819   TimeFrames = 0;
3820   TimePlayed = 0;
3821   TimeLeft = level.time;
3822   TapeTime = 0;
3823
3824   ScreenMovDir = MV_NONE;
3825   ScreenMovPos = 0;
3826   ScreenGfxPos = 0;
3827
3828   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3829
3830   AllPlayersGone = FALSE;
3831
3832   game.yamyam_content_nr = 0;
3833   game.robot_wheel_active = FALSE;
3834   game.magic_wall_active = FALSE;
3835   game.magic_wall_time_left = 0;
3836   game.light_time_left = 0;
3837   game.timegate_time_left = 0;
3838   game.switchgate_pos = 0;
3839   game.wind_direction = level.wind_direction_initial;
3840
3841 #if !USE_PLAYER_GRAVITY
3842   game.gravity = FALSE;
3843   game.explosions_delayed = TRUE;
3844 #endif
3845
3846   game.lenses_time_left = 0;
3847   game.magnify_time_left = 0;
3848
3849   game.ball_state = level.ball_state_initial;
3850   game.ball_content_nr = 0;
3851
3852   game.envelope_active = FALSE;
3853
3854   /* set focus to local player for network games, else to all players */
3855   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3856   game.centered_player_nr_next = game.centered_player_nr;
3857   game.set_centered_player = FALSE;
3858
3859   if (network_playing && tape.recording)
3860   {
3861     /* store client dependent player focus when recording network games */
3862     tape.centered_player_nr_next = game.centered_player_nr_next;
3863     tape.set_centered_player = TRUE;
3864   }
3865
3866   for (i = 0; i < NUM_BELTS; i++)
3867   {
3868     game.belt_dir[i] = MV_NONE;
3869     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3870   }
3871
3872   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3873     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3874
3875   SCAN_PLAYFIELD(x, y)
3876   {
3877     Feld[x][y] = level.field[x][y];
3878     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3879     ChangeDelay[x][y] = 0;
3880     ChangePage[x][y] = -1;
3881 #if USE_NEW_CUSTOM_VALUE
3882     CustomValue[x][y] = 0;              /* initialized in InitField() */
3883 #endif
3884     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3885     AmoebaNr[x][y] = 0;
3886     WasJustMoving[x][y] = 0;
3887     WasJustFalling[x][y] = 0;
3888     CheckCollision[x][y] = 0;
3889     CheckImpact[x][y] = 0;
3890     Stop[x][y] = FALSE;
3891     Pushed[x][y] = FALSE;
3892
3893     ChangeCount[x][y] = 0;
3894     ChangeEvent[x][y] = -1;
3895
3896     ExplodePhase[x][y] = 0;
3897     ExplodeDelay[x][y] = 0;
3898     ExplodeField[x][y] = EX_TYPE_NONE;
3899
3900     RunnerVisit[x][y] = 0;
3901     PlayerVisit[x][y] = 0;
3902
3903     GfxFrame[x][y] = 0;
3904     GfxRandom[x][y] = INIT_GFX_RANDOM();
3905     GfxElement[x][y] = EL_UNDEFINED;
3906     GfxAction[x][y] = ACTION_DEFAULT;
3907     GfxDir[x][y] = MV_NONE;
3908     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3909   }
3910
3911   SCAN_PLAYFIELD(x, y)
3912   {
3913     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3914       emulate_bd = FALSE;
3915     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3916       emulate_sb = FALSE;
3917     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3918       emulate_sp = FALSE;
3919
3920     InitField(x, y, TRUE);
3921
3922     ResetGfxAnimation(x, y);
3923   }
3924
3925   InitBeltMovement();
3926
3927   for (i = 0; i < MAX_PLAYERS; i++)
3928   {
3929     struct PlayerInfo *player = &stored_player[i];
3930
3931     /* set number of special actions for bored and sleeping animation */
3932     player->num_special_action_bored =
3933       get_num_special_action(player->artwork_element,
3934                              ACTION_BORING_1, ACTION_BORING_LAST);
3935     player->num_special_action_sleeping =
3936       get_num_special_action(player->artwork_element,
3937                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3938   }
3939
3940   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3941                     emulate_sb ? EMU_SOKOBAN :
3942                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3943
3944 #if USE_NEW_ALL_SLIPPERY
3945   /* initialize type of slippery elements */
3946   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3947   {
3948     if (!IS_CUSTOM_ELEMENT(i))
3949     {
3950       /* default: elements slip down either to the left or right randomly */
3951       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3952
3953       /* SP style elements prefer to slip down on the left side */
3954       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3955         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3956
3957       /* BD style elements prefer to slip down on the left side */
3958       if (game.emulation == EMU_BOULDERDASH)
3959         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3960     }
3961   }
3962 #endif
3963
3964   /* initialize explosion and ignition delay */
3965   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3966   {
3967     if (!IS_CUSTOM_ELEMENT(i))
3968     {
3969       int num_phase = 8;
3970       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3971                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3972                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3973       int last_phase = (num_phase + 1) * delay;
3974       int half_phase = (num_phase / 2) * delay;
3975
3976       element_info[i].explosion_delay = last_phase - 1;
3977       element_info[i].ignition_delay = half_phase;
3978
3979       if (i == EL_BLACK_ORB)
3980         element_info[i].ignition_delay = 1;
3981     }
3982
3983 #if 0
3984     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3985       element_info[i].explosion_delay = 1;
3986
3987     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3988       element_info[i].ignition_delay = 1;
3989 #endif
3990   }
3991
3992   /* correct non-moving belts to start moving left */
3993   for (i = 0; i < NUM_BELTS; i++)
3994     if (game.belt_dir[i] == MV_NONE)
3995       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3996
3997   /* check if any connected player was not found in playfield */
3998   for (i = 0; i < MAX_PLAYERS; i++)
3999   {
4000     struct PlayerInfo *player = &stored_player[i];
4001
4002     if (player->connected && !player->present)
4003     {
4004       for (j = 0; j < MAX_PLAYERS; j++)
4005       {
4006         struct PlayerInfo *some_player = &stored_player[j];
4007         int jx = some_player->jx, jy = some_player->jy;
4008
4009         /* assign first free player found that is present in the playfield */
4010         if (some_player->present && !some_player->connected)
4011         {
4012           player->present = TRUE;
4013           player->active = TRUE;
4014
4015           some_player->present = FALSE;
4016           some_player->active = FALSE;
4017
4018           player->artwork_element = some_player->artwork_element;
4019
4020           player->block_last_field       = some_player->block_last_field;
4021           player->block_delay_adjustment = some_player->block_delay_adjustment;
4022
4023           StorePlayer[jx][jy] = player->element_nr;
4024           player->jx = player->last_jx = jx;
4025           player->jy = player->last_jy = jy;
4026
4027           break;
4028         }
4029       }
4030     }
4031   }
4032
4033   if (tape.playing)
4034   {
4035     /* when playing a tape, eliminate all players who do not participate */
4036
4037     for (i = 0; i < MAX_PLAYERS; i++)
4038     {
4039       if (stored_player[i].active && !tape.player_participates[i])
4040       {
4041         struct PlayerInfo *player = &stored_player[i];
4042         int jx = player->jx, jy = player->jy;
4043
4044         player->active = FALSE;
4045         StorePlayer[jx][jy] = 0;
4046         Feld[jx][jy] = EL_EMPTY;
4047       }
4048     }
4049   }
4050   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4051   {
4052     /* when in single player mode, eliminate all but the first active player */
4053
4054     for (i = 0; i < MAX_PLAYERS; i++)
4055     {
4056       if (stored_player[i].active)
4057       {
4058         for (j = i + 1; j < MAX_PLAYERS; j++)
4059         {
4060           if (stored_player[j].active)
4061           {
4062             struct PlayerInfo *player = &stored_player[j];
4063             int jx = player->jx, jy = player->jy;
4064
4065             player->active = FALSE;
4066             player->present = FALSE;
4067
4068             StorePlayer[jx][jy] = 0;
4069             Feld[jx][jy] = EL_EMPTY;
4070           }
4071         }
4072       }
4073     }
4074   }
4075
4076   /* when recording the game, store which players take part in the game */
4077   if (tape.recording)
4078   {
4079     for (i = 0; i < MAX_PLAYERS; i++)
4080       if (stored_player[i].active)
4081         tape.player_participates[i] = TRUE;
4082   }
4083
4084   if (options.debug)
4085   {
4086     for (i = 0; i < MAX_PLAYERS; i++)
4087     {
4088       struct PlayerInfo *player = &stored_player[i];
4089
4090       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4091              i+1,
4092              player->present,
4093              player->connected,
4094              player->active);
4095       if (local_player == player)
4096         printf("Player  %d is local player.\n", i+1);
4097     }
4098   }
4099
4100   if (BorderElement == EL_EMPTY)
4101   {
4102     SBX_Left = 0;
4103     SBX_Right = lev_fieldx - SCR_FIELDX;
4104     SBY_Upper = 0;
4105     SBY_Lower = lev_fieldy - SCR_FIELDY;
4106   }
4107   else
4108   {
4109     SBX_Left = -1;
4110     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4111     SBY_Upper = -1;
4112     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4113   }
4114
4115   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4116     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4117
4118   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4119     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4120
4121   /* if local player not found, look for custom element that might create
4122      the player (make some assumptions about the right custom element) */
4123   if (!local_player->present)
4124   {
4125     int start_x = 0, start_y = 0;
4126     int found_rating = 0;
4127     int found_element = EL_UNDEFINED;
4128     int player_nr = local_player->index_nr;
4129
4130     SCAN_PLAYFIELD(x, y)
4131     {
4132       int element = Feld[x][y];
4133       int content;
4134       int xx, yy;
4135       boolean is_player;
4136
4137       if (level.use_start_element[player_nr] &&
4138           level.start_element[player_nr] == element &&
4139           found_rating < 4)
4140       {
4141         start_x = x;
4142         start_y = y;
4143
4144         found_rating = 4;
4145         found_element = element;
4146       }
4147
4148       if (!IS_CUSTOM_ELEMENT(element))
4149         continue;
4150
4151       if (CAN_CHANGE(element))
4152       {
4153         for (i = 0; i < element_info[element].num_change_pages; i++)
4154         {
4155           /* check for player created from custom element as single target */
4156           content = element_info[element].change_page[i].target_element;
4157           is_player = ELEM_IS_PLAYER(content);
4158
4159           if (is_player && (found_rating < 3 ||
4160                             (found_rating == 3 && element < found_element)))
4161           {
4162             start_x = x;
4163             start_y = y;
4164
4165             found_rating = 3;
4166             found_element = element;
4167           }
4168         }
4169       }
4170
4171       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4172       {
4173         /* check for player created from custom element as explosion content */
4174         content = element_info[element].content.e[xx][yy];
4175         is_player = ELEM_IS_PLAYER(content);
4176
4177         if (is_player && (found_rating < 2 ||
4178                           (found_rating == 2 && element < found_element)))
4179         {
4180           start_x = x + xx - 1;
4181           start_y = y + yy - 1;
4182
4183           found_rating = 2;
4184           found_element = element;
4185         }
4186
4187         if (!CAN_CHANGE(element))
4188           continue;
4189
4190         for (i = 0; i < element_info[element].num_change_pages; i++)
4191         {
4192           /* check for player created from custom element as extended target */
4193           content =
4194             element_info[element].change_page[i].target_content.e[xx][yy];
4195
4196           is_player = ELEM_IS_PLAYER(content);
4197
4198           if (is_player && (found_rating < 1 ||
4199                             (found_rating == 1 && element < found_element)))
4200           {
4201             start_x = x + xx - 1;
4202             start_y = y + yy - 1;
4203
4204             found_rating = 1;
4205             found_element = element;
4206           }
4207         }
4208       }
4209     }
4210
4211     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4212                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4213                 start_x - MIDPOSX);
4214
4215     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4216                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4217                 start_y - MIDPOSY);
4218   }
4219   else
4220   {
4221     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4222                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4223                 local_player->jx - MIDPOSX);
4224
4225     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4226                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4227                 local_player->jy - MIDPOSY);
4228   }
4229
4230 #if 0
4231   /* do not use PLAYING mask for fading out from main screen */
4232   game_status = GAME_MODE_MAIN;
4233 #endif
4234
4235   StopAnimation();
4236
4237   if (!game.restart_level)
4238     CloseDoor(DOOR_CLOSE_1);
4239
4240 #if 1
4241   if (level_editor_test_game)
4242     FadeSkipNextFadeIn();
4243   else
4244     FadeSetEnterScreen();
4245 #else
4246   if (level_editor_test_game)
4247     fading = fading_none;
4248   else
4249     fading = menu.destination;
4250 #endif
4251
4252 #if 1
4253   FadeOut(REDRAW_FIELD);
4254 #else
4255   if (do_fading)
4256     FadeOut(REDRAW_FIELD);
4257 #endif
4258
4259 #if 0
4260   game_status = GAME_MODE_PLAYING;
4261 #endif
4262
4263   /* !!! FIX THIS (START) !!! */
4264   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4265   {
4266     InitGameEngine_EM();
4267
4268     /* blit playfield from scroll buffer to normal back buffer for fading in */
4269     BlitScreenToBitmap_EM(backbuffer);
4270   }
4271   else
4272   {
4273     DrawLevel();
4274     DrawAllPlayers();
4275
4276     /* after drawing the level, correct some elements */
4277     if (game.timegate_time_left == 0)
4278       CloseAllOpenTimegates();
4279
4280     /* blit playfield from scroll buffer to normal back buffer for fading in */
4281     if (setup.soft_scrolling)
4282       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4283
4284     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4285   }
4286   /* !!! FIX THIS (END) !!! */
4287
4288 #if 1
4289   FadeIn(REDRAW_FIELD);
4290 #else
4291   if (do_fading)
4292     FadeIn(REDRAW_FIELD);
4293
4294   BackToFront();
4295 #endif
4296
4297   if (!game.restart_level)
4298   {
4299     /* copy default game door content to main double buffer */
4300     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4301                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4302   }
4303
4304   SetPanelBackground();
4305   SetDrawBackgroundMask(REDRAW_DOOR_1);
4306
4307 #if 1
4308   UpdateAndDisplayGameControlValues();
4309 #else
4310   UpdateGameDoorValues();
4311   DrawGameDoorValues();
4312 #endif
4313
4314   if (!game.restart_level)
4315   {
4316     UnmapGameButtons();
4317     UnmapTapeButtons();
4318     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4319     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4320     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4321     MapGameButtons();
4322     MapTapeButtons();
4323
4324     /* copy actual game door content to door double buffer for OpenDoor() */
4325     BlitBitmap(drawto, bitmap_db_door,
4326                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4327
4328     OpenDoor(DOOR_OPEN_ALL);
4329
4330     PlaySound(SND_GAME_STARTING);
4331
4332     if (setup.sound_music)
4333       PlayLevelMusic();
4334
4335     KeyboardAutoRepeatOffUnlessAutoplay();
4336
4337     if (options.debug)
4338     {
4339       for (i = 0; i < MAX_PLAYERS; i++)
4340         printf("Player %d %sactive.\n",
4341                i + 1, (stored_player[i].active ? "" : "not "));
4342     }
4343   }
4344
4345 #if 1
4346   UnmapAllGadgets();
4347
4348   MapGameButtons();
4349   MapTapeButtons();
4350 #endif
4351
4352   game.restart_level = FALSE;
4353 }
4354
4355 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4356 {
4357   /* this is used for non-R'n'D game engines to update certain engine values */
4358
4359   /* needed to determine if sounds are played within the visible screen area */
4360   scroll_x = actual_scroll_x;
4361   scroll_y = actual_scroll_y;
4362 }
4363
4364 void InitMovDir(int x, int y)
4365 {
4366   int i, element = Feld[x][y];
4367   static int xy[4][2] =
4368   {
4369     {  0, +1 },
4370     { +1,  0 },
4371     {  0, -1 },
4372     { -1,  0 }
4373   };
4374   static int direction[3][4] =
4375   {
4376     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4377     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4378     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4379   };
4380
4381   switch (element)
4382   {
4383     case EL_BUG_RIGHT:
4384     case EL_BUG_UP:
4385     case EL_BUG_LEFT:
4386     case EL_BUG_DOWN:
4387       Feld[x][y] = EL_BUG;
4388       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4389       break;
4390
4391     case EL_SPACESHIP_RIGHT:
4392     case EL_SPACESHIP_UP:
4393     case EL_SPACESHIP_LEFT:
4394     case EL_SPACESHIP_DOWN:
4395       Feld[x][y] = EL_SPACESHIP;
4396       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4397       break;
4398
4399     case EL_BD_BUTTERFLY_RIGHT:
4400     case EL_BD_BUTTERFLY_UP:
4401     case EL_BD_BUTTERFLY_LEFT:
4402     case EL_BD_BUTTERFLY_DOWN:
4403       Feld[x][y] = EL_BD_BUTTERFLY;
4404       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4405       break;
4406
4407     case EL_BD_FIREFLY_RIGHT:
4408     case EL_BD_FIREFLY_UP:
4409     case EL_BD_FIREFLY_LEFT:
4410     case EL_BD_FIREFLY_DOWN:
4411       Feld[x][y] = EL_BD_FIREFLY;
4412       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4413       break;
4414
4415     case EL_PACMAN_RIGHT:
4416     case EL_PACMAN_UP:
4417     case EL_PACMAN_LEFT:
4418     case EL_PACMAN_DOWN:
4419       Feld[x][y] = EL_PACMAN;
4420       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4421       break;
4422
4423     case EL_YAMYAM_LEFT:
4424     case EL_YAMYAM_RIGHT:
4425     case EL_YAMYAM_UP:
4426     case EL_YAMYAM_DOWN:
4427       Feld[x][y] = EL_YAMYAM;
4428       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4429       break;
4430
4431     case EL_SP_SNIKSNAK:
4432       MovDir[x][y] = MV_UP;
4433       break;
4434
4435     case EL_SP_ELECTRON:
4436       MovDir[x][y] = MV_LEFT;
4437       break;
4438
4439     case EL_MOLE_LEFT:
4440     case EL_MOLE_RIGHT:
4441     case EL_MOLE_UP:
4442     case EL_MOLE_DOWN:
4443       Feld[x][y] = EL_MOLE;
4444       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4445       break;
4446
4447     default:
4448       if (IS_CUSTOM_ELEMENT(element))
4449       {
4450         struct ElementInfo *ei = &element_info[element];
4451         int move_direction_initial = ei->move_direction_initial;
4452         int move_pattern = ei->move_pattern;
4453
4454         if (move_direction_initial == MV_START_PREVIOUS)
4455         {
4456           if (MovDir[x][y] != MV_NONE)
4457             return;
4458
4459           move_direction_initial = MV_START_AUTOMATIC;
4460         }
4461
4462         if (move_direction_initial == MV_START_RANDOM)
4463           MovDir[x][y] = 1 << RND(4);
4464         else if (move_direction_initial & MV_ANY_DIRECTION)
4465           MovDir[x][y] = move_direction_initial;
4466         else if (move_pattern == MV_ALL_DIRECTIONS ||
4467                  move_pattern == MV_TURNING_LEFT ||
4468                  move_pattern == MV_TURNING_RIGHT ||
4469                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4470                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4471                  move_pattern == MV_TURNING_RANDOM)
4472           MovDir[x][y] = 1 << RND(4);
4473         else if (move_pattern == MV_HORIZONTAL)
4474           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4475         else if (move_pattern == MV_VERTICAL)
4476           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4477         else if (move_pattern & MV_ANY_DIRECTION)
4478           MovDir[x][y] = element_info[element].move_pattern;
4479         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4480                  move_pattern == MV_ALONG_RIGHT_SIDE)
4481         {
4482           /* use random direction as default start direction */
4483           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4484             MovDir[x][y] = 1 << RND(4);
4485
4486           for (i = 0; i < NUM_DIRECTIONS; i++)
4487           {
4488             int x1 = x + xy[i][0];
4489             int y1 = y + xy[i][1];
4490
4491             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4492             {
4493               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4494                 MovDir[x][y] = direction[0][i];
4495               else
4496                 MovDir[x][y] = direction[1][i];
4497
4498               break;
4499             }
4500           }
4501         }                
4502       }
4503       else
4504       {
4505         MovDir[x][y] = 1 << RND(4);
4506
4507         if (element != EL_BUG &&
4508             element != EL_SPACESHIP &&
4509             element != EL_BD_BUTTERFLY &&
4510             element != EL_BD_FIREFLY)
4511           break;
4512
4513         for (i = 0; i < NUM_DIRECTIONS; i++)
4514         {
4515           int x1 = x + xy[i][0];
4516           int y1 = y + xy[i][1];
4517
4518           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4519           {
4520             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4521             {
4522               MovDir[x][y] = direction[0][i];
4523               break;
4524             }
4525             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4526                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4527             {
4528               MovDir[x][y] = direction[1][i];
4529               break;
4530             }
4531           }
4532         }
4533       }
4534       break;
4535   }
4536
4537   GfxDir[x][y] = MovDir[x][y];
4538 }
4539
4540 void InitAmoebaNr(int x, int y)
4541 {
4542   int i;
4543   int group_nr = AmoebeNachbarNr(x, y);
4544
4545   if (group_nr == 0)
4546   {
4547     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4548     {
4549       if (AmoebaCnt[i] == 0)
4550       {
4551         group_nr = i;
4552         break;
4553       }
4554     }
4555   }
4556
4557   AmoebaNr[x][y] = group_nr;
4558   AmoebaCnt[group_nr]++;
4559   AmoebaCnt2[group_nr]++;
4560 }
4561
4562 static void PlayerWins(struct PlayerInfo *player)
4563 {
4564   player->LevelSolved = TRUE;
4565   player->GameOver = TRUE;
4566
4567   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4568                          level.native_em_level->lev->score : player->score);
4569
4570   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4571   player->LevelSolved_CountingScore = player->score_final;
4572 }
4573
4574 void GameWon()
4575 {
4576   static int time, time_final;
4577   static int score, score_final;
4578   static int game_over_delay_1 = 0;
4579   static int game_over_delay_2 = 0;
4580   int game_over_delay_value_1 = 50;
4581   int game_over_delay_value_2 = 50;
4582
4583   if (!local_player->LevelSolved_GameWon)
4584   {
4585     int i;
4586
4587     /* do not start end game actions before the player stops moving (to exit) */
4588     if (local_player->MovPos)
4589       return;
4590
4591     local_player->LevelSolved_GameWon = TRUE;
4592     local_player->LevelSolved_SaveTape = tape.recording;
4593     local_player->LevelSolved_SaveScore = !tape.playing;
4594
4595     if (tape.auto_play)         /* tape might already be stopped here */
4596       tape.auto_play_level_solved = TRUE;
4597
4598 #if 1
4599     TapeStop();
4600 #endif
4601
4602     game_over_delay_1 = game_over_delay_value_1;
4603     game_over_delay_2 = game_over_delay_value_2;
4604
4605     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4606     score = score_final = local_player->score_final;
4607
4608     if (TimeLeft > 0)
4609     {
4610       time_final = 0;
4611       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4612     }
4613     else if (level.time == 0 && TimePlayed < 999)
4614     {
4615       time_final = 999;
4616       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4617     }
4618
4619     local_player->score_final = score_final;
4620
4621     if (level_editor_test_game)
4622     {
4623       time = time_final;
4624       score = score_final;
4625
4626 #if 1
4627       local_player->LevelSolved_CountingTime = time;
4628       local_player->LevelSolved_CountingScore = score;
4629
4630       game_panel_controls[GAME_PANEL_TIME].value = time;
4631       game_panel_controls[GAME_PANEL_SCORE].value = score;
4632
4633       DisplayGameControlValues();
4634 #else
4635       DrawGameValue_Time(time);
4636       DrawGameValue_Score(score);
4637 #endif
4638     }
4639
4640     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4641     {
4642       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4643       {
4644         /* close exit door after last player */
4645         if ((AllPlayersGone &&
4646              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4647               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4648               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4649             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4650             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4651         {
4652           int element = Feld[ExitX][ExitY];
4653
4654 #if 0
4655           if (element == EL_EM_EXIT_OPEN ||
4656               element == EL_EM_STEEL_EXIT_OPEN)
4657           {
4658             Bang(ExitX, ExitY);
4659           }
4660           else
4661 #endif
4662           {
4663             Feld[ExitX][ExitY] =
4664               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4665                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4666                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4667                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4668                EL_EM_STEEL_EXIT_CLOSING);
4669
4670             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4671           }
4672         }
4673
4674         /* player disappears */
4675         DrawLevelField(ExitX, ExitY);
4676       }
4677
4678       for (i = 0; i < MAX_PLAYERS; i++)
4679       {
4680         struct PlayerInfo *player = &stored_player[i];
4681
4682         if (player->present)
4683         {
4684           RemovePlayer(player);
4685
4686           /* player disappears */
4687           DrawLevelField(player->jx, player->jy);
4688         }
4689       }
4690     }
4691
4692     PlaySound(SND_GAME_WINNING);
4693   }
4694
4695   if (game_over_delay_1 > 0)
4696   {
4697     game_over_delay_1--;
4698
4699     return;
4700   }
4701
4702   if (time != time_final)
4703   {
4704     int time_to_go = ABS(time_final - time);
4705     int time_count_dir = (time < time_final ? +1 : -1);
4706     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4707
4708     time  += time_count_steps * time_count_dir;
4709     score += time_count_steps * level.score[SC_TIME_BONUS];
4710
4711 #if 1
4712     local_player->LevelSolved_CountingTime = time;
4713     local_player->LevelSolved_CountingScore = score;
4714
4715     game_panel_controls[GAME_PANEL_TIME].value = time;
4716     game_panel_controls[GAME_PANEL_SCORE].value = score;
4717
4718     DisplayGameControlValues();
4719 #else
4720     DrawGameValue_Time(time);
4721     DrawGameValue_Score(score);
4722 #endif
4723
4724     if (time == time_final)
4725       StopSound(SND_GAME_LEVELTIME_BONUS);
4726     else if (setup.sound_loops)
4727       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4728     else
4729       PlaySound(SND_GAME_LEVELTIME_BONUS);
4730
4731     return;
4732   }
4733
4734   local_player->LevelSolved_PanelOff = TRUE;
4735
4736   if (game_over_delay_2 > 0)
4737   {
4738     game_over_delay_2--;
4739
4740     return;
4741   }
4742
4743 #if 1
4744   GameEnd();
4745 #endif
4746 }
4747
4748 void GameEnd()
4749 {
4750   int hi_pos;
4751   boolean raise_level = FALSE;
4752
4753   local_player->LevelSolved_GameEnd = TRUE;
4754
4755   CloseDoor(DOOR_CLOSE_1);
4756
4757   if (local_player->LevelSolved_SaveTape)
4758   {
4759 #if 0
4760     TapeStop();
4761 #endif
4762
4763 #if 1
4764     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4765 #else
4766     SaveTape(tape.level_nr);            /* ask to save tape */
4767 #endif
4768   }
4769
4770   if (level_editor_test_game)
4771   {
4772     game_status = GAME_MODE_MAIN;
4773
4774 #if 1
4775     DrawAndFadeInMainMenu(REDRAW_FIELD);
4776 #else
4777     DrawMainMenu();
4778 #endif
4779
4780     return;
4781   }
4782
4783   if (!local_player->LevelSolved_SaveScore)
4784   {
4785 #if 1
4786     FadeOut(REDRAW_FIELD);
4787 #endif
4788
4789     game_status = GAME_MODE_MAIN;
4790
4791     DrawAndFadeInMainMenu(REDRAW_FIELD);
4792
4793     return;
4794   }
4795
4796   if (level_nr == leveldir_current->handicap_level)
4797   {
4798     leveldir_current->handicap_level++;
4799     SaveLevelSetup_SeriesInfo();
4800   }
4801
4802   if (level_nr < leveldir_current->last_level)
4803     raise_level = TRUE;                 /* advance to next level */
4804
4805   if ((hi_pos = NewHiScore()) >= 0) 
4806   {
4807     game_status = GAME_MODE_SCORES;
4808
4809     DrawHallOfFame(hi_pos);
4810
4811     if (raise_level)
4812     {
4813       level_nr++;
4814       TapeErase();
4815     }
4816   }
4817   else
4818   {
4819 #if 1
4820     FadeOut(REDRAW_FIELD);
4821 #endif
4822
4823     game_status = GAME_MODE_MAIN;
4824
4825     if (raise_level)
4826     {
4827       level_nr++;
4828       TapeErase();
4829     }
4830
4831     DrawAndFadeInMainMenu(REDRAW_FIELD);
4832   }
4833 }
4834
4835 int NewHiScore()
4836 {
4837   int k, l;
4838   int position = -1;
4839
4840   LoadScore(level_nr);
4841
4842   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4843       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4844     return -1;
4845
4846   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4847   {
4848     if (local_player->score_final > highscore[k].Score)
4849     {
4850       /* player has made it to the hall of fame */
4851
4852       if (k < MAX_SCORE_ENTRIES - 1)
4853       {
4854         int m = MAX_SCORE_ENTRIES - 1;
4855
4856 #ifdef ONE_PER_NAME
4857         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4858           if (strEqual(setup.player_name, highscore[l].Name))
4859             m = l;
4860         if (m == k)     /* player's new highscore overwrites his old one */
4861           goto put_into_list;
4862 #endif
4863
4864         for (l = m; l > k; l--)
4865         {
4866           strcpy(highscore[l].Name, highscore[l - 1].Name);
4867           highscore[l].Score = highscore[l - 1].Score;
4868         }
4869       }
4870
4871 #ifdef ONE_PER_NAME
4872       put_into_list:
4873 #endif
4874       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4875       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4876       highscore[k].Score = local_player->score_final; 
4877       position = k;
4878       break;
4879     }
4880
4881 #ifdef ONE_PER_NAME
4882     else if (!strncmp(setup.player_name, highscore[k].Name,
4883                       MAX_PLAYER_NAME_LEN))
4884       break;    /* player already there with a higher score */
4885 #endif
4886
4887   }
4888
4889   if (position >= 0) 
4890     SaveScore(level_nr);
4891
4892   return position;
4893 }
4894
4895 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4896 {
4897   int element = Feld[x][y];
4898   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4899   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4900   int horiz_move = (dx != 0);
4901   int sign = (horiz_move ? dx : dy);
4902   int step = sign * element_info[element].move_stepsize;
4903
4904   /* special values for move stepsize for spring and things on conveyor belt */
4905   if (horiz_move)
4906   {
4907     if (CAN_FALL(element) &&
4908         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4909       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4910     else if (element == EL_SPRING)
4911       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4912   }
4913
4914   return step;
4915 }
4916
4917 inline static int getElementMoveStepsize(int x, int y)
4918 {
4919   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4920 }
4921
4922 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4923 {
4924   if (player->GfxAction != action || player->GfxDir != dir)
4925   {
4926 #if 0
4927     printf("Player frame reset! (%d => %d, %d => %d)\n",
4928            player->GfxAction, action, player->GfxDir, dir);
4929 #endif
4930
4931     player->GfxAction = action;
4932     player->GfxDir = dir;
4933     player->Frame = 0;
4934     player->StepFrame = 0;
4935   }
4936 }
4937
4938 #if USE_GFX_RESET_GFX_ANIMATION
4939 static void ResetGfxFrame(int x, int y, boolean redraw)
4940 {
4941   int element = Feld[x][y];
4942   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4943   int last_gfx_frame = GfxFrame[x][y];
4944
4945   if (graphic_info[graphic].anim_global_sync)
4946     GfxFrame[x][y] = FrameCounter;
4947   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4948     GfxFrame[x][y] = CustomValue[x][y];
4949   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4950     GfxFrame[x][y] = element_info[element].collect_score;
4951   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4952     GfxFrame[x][y] = ChangeDelay[x][y];
4953
4954   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4955     DrawLevelGraphicAnimation(x, y, graphic);
4956 }
4957 #endif
4958
4959 static void ResetGfxAnimation(int x, int y)
4960 {
4961   GfxAction[x][y] = ACTION_DEFAULT;
4962   GfxDir[x][y] = MovDir[x][y];
4963   GfxFrame[x][y] = 0;
4964
4965 #if USE_GFX_RESET_GFX_ANIMATION
4966   ResetGfxFrame(x, y, FALSE);
4967 #endif
4968 }
4969
4970 static void ResetRandomAnimationValue(int x, int y)
4971 {
4972   GfxRandom[x][y] = INIT_GFX_RANDOM();
4973 }
4974
4975 void InitMovingField(int x, int y, int direction)
4976 {
4977   int element = Feld[x][y];
4978   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4979   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4980   int newx = x + dx;
4981   int newy = y + dy;
4982   boolean is_moving_before, is_moving_after;
4983 #if 0
4984   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4985 #endif
4986
4987   /* check if element was/is moving or being moved before/after mode change */
4988 #if 1
4989 #if 1
4990   is_moving_before = (WasJustMoving[x][y] != 0);
4991 #else
4992   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4993   is_moving_before = WasJustMoving[x][y];
4994 #endif
4995 #else
4996   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4997 #endif
4998   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4999
5000   /* reset animation only for moving elements which change direction of moving
5001      or which just started or stopped moving
5002      (else CEs with property "can move" / "not moving" are reset each frame) */
5003 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5004 #if 1
5005   if (is_moving_before != is_moving_after ||
5006       direction != MovDir[x][y])
5007     ResetGfxAnimation(x, y);
5008 #else
5009   if ((is_moving_before || is_moving_after) && !continues_moving)
5010     ResetGfxAnimation(x, y);
5011 #endif
5012 #else
5013   if (!continues_moving)
5014     ResetGfxAnimation(x, y);
5015 #endif
5016
5017   MovDir[x][y] = direction;
5018   GfxDir[x][y] = direction;
5019
5020 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5021   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5022                      direction == MV_DOWN && CAN_FALL(element) ?
5023                      ACTION_FALLING : ACTION_MOVING);
5024 #else
5025   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5026                      ACTION_FALLING : ACTION_MOVING);
5027 #endif
5028
5029   /* this is needed for CEs with property "can move" / "not moving" */
5030
5031   if (is_moving_after)
5032   {
5033     if (Feld[newx][newy] == EL_EMPTY)
5034       Feld[newx][newy] = EL_BLOCKED;
5035
5036     MovDir[newx][newy] = MovDir[x][y];
5037
5038 #if USE_NEW_CUSTOM_VALUE
5039     CustomValue[newx][newy] = CustomValue[x][y];
5040 #endif
5041
5042     GfxFrame[newx][newy] = GfxFrame[x][y];
5043     GfxRandom[newx][newy] = GfxRandom[x][y];
5044     GfxAction[newx][newy] = GfxAction[x][y];
5045     GfxDir[newx][newy] = GfxDir[x][y];
5046   }
5047 }
5048
5049 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5050 {
5051   int direction = MovDir[x][y];
5052   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5053   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5054
5055   *goes_to_x = newx;
5056   *goes_to_y = newy;
5057 }
5058
5059 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5060 {
5061   int oldx = x, oldy = y;
5062   int direction = MovDir[x][y];
5063
5064   if (direction == MV_LEFT)
5065     oldx++;
5066   else if (direction == MV_RIGHT)
5067     oldx--;
5068   else if (direction == MV_UP)
5069     oldy++;
5070   else if (direction == MV_DOWN)
5071     oldy--;
5072
5073   *comes_from_x = oldx;
5074   *comes_from_y = oldy;
5075 }
5076
5077 int MovingOrBlocked2Element(int x, int y)
5078 {
5079   int element = Feld[x][y];
5080
5081   if (element == EL_BLOCKED)
5082   {
5083     int oldx, oldy;
5084
5085     Blocked2Moving(x, y, &oldx, &oldy);
5086     return Feld[oldx][oldy];
5087   }
5088   else
5089     return element;
5090 }
5091
5092 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5093 {
5094   /* like MovingOrBlocked2Element(), but if element is moving
5095      and (x,y) is the field the moving element is just leaving,
5096      return EL_BLOCKED instead of the element value */
5097   int element = Feld[x][y];
5098
5099   if (IS_MOVING(x, y))
5100   {
5101     if (element == EL_BLOCKED)
5102     {
5103       int oldx, oldy;
5104
5105       Blocked2Moving(x, y, &oldx, &oldy);
5106       return Feld[oldx][oldy];
5107     }
5108     else
5109       return EL_BLOCKED;
5110   }
5111   else
5112     return element;
5113 }
5114
5115 static void RemoveField(int x, int y)
5116 {
5117   Feld[x][y] = EL_EMPTY;
5118
5119   MovPos[x][y] = 0;
5120   MovDir[x][y] = 0;
5121   MovDelay[x][y] = 0;
5122
5123 #if USE_NEW_CUSTOM_VALUE
5124   CustomValue[x][y] = 0;
5125 #endif
5126
5127   AmoebaNr[x][y] = 0;
5128   ChangeDelay[x][y] = 0;
5129   ChangePage[x][y] = -1;
5130   Pushed[x][y] = FALSE;
5131
5132 #if 0
5133   ExplodeField[x][y] = EX_TYPE_NONE;
5134 #endif
5135
5136   GfxElement[x][y] = EL_UNDEFINED;
5137   GfxAction[x][y] = ACTION_DEFAULT;
5138   GfxDir[x][y] = MV_NONE;
5139   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5140 }
5141
5142 void RemoveMovingField(int x, int y)
5143 {
5144   int oldx = x, oldy = y, newx = x, newy = y;
5145   int element = Feld[x][y];
5146   int next_element = EL_UNDEFINED;
5147
5148   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5149     return;
5150
5151   if (IS_MOVING(x, y))
5152   {
5153     Moving2Blocked(x, y, &newx, &newy);
5154
5155     if (Feld[newx][newy] != EL_BLOCKED)
5156     {
5157       /* element is moving, but target field is not free (blocked), but
5158          already occupied by something different (example: acid pool);
5159          in this case, only remove the moving field, but not the target */
5160
5161       RemoveField(oldx, oldy);
5162
5163       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5164
5165       TEST_DrawLevelField(oldx, oldy);
5166
5167       return;
5168     }
5169   }
5170   else if (element == EL_BLOCKED)
5171   {
5172     Blocked2Moving(x, y, &oldx, &oldy);
5173     if (!IS_MOVING(oldx, oldy))
5174       return;
5175   }
5176
5177   if (element == EL_BLOCKED &&
5178       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5179        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5180        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5181        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5182        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5183        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5184     next_element = get_next_element(Feld[oldx][oldy]);
5185
5186   RemoveField(oldx, oldy);
5187   RemoveField(newx, newy);
5188
5189   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5190
5191   if (next_element != EL_UNDEFINED)
5192     Feld[oldx][oldy] = next_element;
5193
5194   TEST_DrawLevelField(oldx, oldy);
5195   TEST_DrawLevelField(newx, newy);
5196 }
5197
5198 void DrawDynamite(int x, int y)
5199 {
5200   int sx = SCREENX(x), sy = SCREENY(y);
5201   int graphic = el2img(Feld[x][y]);
5202   int frame;
5203
5204   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5205     return;
5206
5207   if (IS_WALKABLE_INSIDE(Back[x][y]))
5208     return;
5209
5210   if (Back[x][y])
5211     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5212   else if (Store[x][y])
5213     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5214
5215   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5216
5217   if (Back[x][y] || Store[x][y])
5218     DrawGraphicThruMask(sx, sy, graphic, frame);
5219   else
5220     DrawGraphic(sx, sy, graphic, frame);
5221 }
5222
5223 void CheckDynamite(int x, int y)
5224 {
5225   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5226   {
5227     MovDelay[x][y]--;
5228
5229     if (MovDelay[x][y] != 0)
5230     {
5231       DrawDynamite(x, y);
5232       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5233
5234       return;
5235     }
5236   }
5237
5238   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5239
5240   Bang(x, y);
5241 }
5242
5243 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5244 {
5245   boolean num_checked_players = 0;
5246   int i;
5247
5248   for (i = 0; i < MAX_PLAYERS; i++)
5249   {
5250     if (stored_player[i].active)
5251     {
5252       int sx = stored_player[i].jx;
5253       int sy = stored_player[i].jy;
5254
5255       if (num_checked_players == 0)
5256       {
5257         *sx1 = *sx2 = sx;
5258         *sy1 = *sy2 = sy;
5259       }
5260       else
5261       {
5262         *sx1 = MIN(*sx1, sx);
5263         *sy1 = MIN(*sy1, sy);
5264         *sx2 = MAX(*sx2, sx);
5265         *sy2 = MAX(*sy2, sy);
5266       }
5267
5268       num_checked_players++;
5269     }
5270   }
5271 }
5272
5273 static boolean checkIfAllPlayersFitToScreen_RND()
5274 {
5275   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5276
5277   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5278
5279   return (sx2 - sx1 < SCR_FIELDX &&
5280           sy2 - sy1 < SCR_FIELDY);
5281 }
5282
5283 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5284 {
5285   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5286
5287   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5288
5289   *sx = (sx1 + sx2) / 2;
5290   *sy = (sy1 + sy2) / 2;
5291 }
5292
5293 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5294                         boolean center_screen, boolean quick_relocation)
5295 {
5296   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5297   boolean no_delay = (tape.warp_forward);
5298   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5299   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5300
5301   if (quick_relocation)
5302   {
5303     int offset = game.scroll_delay_value;
5304
5305     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5306     {
5307       if (!level.shifted_relocation || center_screen)
5308       {
5309         /* quick relocation (without scrolling), with centering of screen */
5310
5311         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5312                     x > SBX_Right + MIDPOSX ? SBX_Right :
5313                     x - MIDPOSX);
5314
5315         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5316                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5317                     y - MIDPOSY);
5318       }
5319       else
5320       {
5321         /* quick relocation (without scrolling), but do not center screen */
5322
5323         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5324                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5325                                old_x - MIDPOSX);
5326
5327         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5328                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5329                                old_y - MIDPOSY);
5330
5331         int offset_x = x + (scroll_x - center_scroll_x);
5332         int offset_y = y + (scroll_y - center_scroll_y);
5333
5334         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5335                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5336                     offset_x - MIDPOSX);
5337
5338         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5339                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5340                     offset_y - MIDPOSY);
5341       }
5342     }
5343     else
5344     {
5345       /* quick relocation (without scrolling), inside visible screen area */
5346
5347       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5348           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5349         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5350
5351       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5352           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5353         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5354
5355       /* don't scroll over playfield boundaries */
5356       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5357         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5358
5359       /* don't scroll over playfield boundaries */
5360       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5361         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5362     }
5363
5364     RedrawPlayfield(TRUE, 0,0,0,0);
5365   }
5366   else
5367   {
5368 #if 1
5369     int scroll_xx, scroll_yy;
5370
5371     if (!level.shifted_relocation || center_screen)
5372     {
5373       /* visible relocation (with scrolling), with centering of screen */
5374
5375       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5376                    x > SBX_Right + MIDPOSX ? SBX_Right :
5377                    x - MIDPOSX);
5378
5379       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5380                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5381                    y - MIDPOSY);
5382     }
5383     else
5384     {
5385       /* visible relocation (with scrolling), but do not center screen */
5386
5387       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5388                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5389                              old_x - MIDPOSX);
5390
5391       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5392                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5393                              old_y - MIDPOSY);
5394
5395       int offset_x = x + (scroll_x - center_scroll_x);
5396       int offset_y = y + (scroll_y - center_scroll_y);
5397
5398       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5399                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5400                    offset_x - MIDPOSX);
5401
5402       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5403                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5404                    offset_y - MIDPOSY);
5405     }
5406
5407 #else
5408
5409     /* visible relocation (with scrolling), with centering of screen */
5410
5411     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5412                      x > SBX_Right + MIDPOSX ? SBX_Right :
5413                      x - MIDPOSX);
5414
5415     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5416                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5417                      y - MIDPOSY);
5418 #endif
5419
5420     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5421
5422     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5423     {
5424       int dx = 0, dy = 0;
5425       int fx = FX, fy = FY;
5426
5427       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5428       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5429
5430       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5431         break;
5432
5433       scroll_x -= dx;
5434       scroll_y -= dy;
5435
5436       fx += dx * TILEX / 2;
5437       fy += dy * TILEY / 2;
5438
5439       ScrollLevel(dx, dy);
5440       DrawAllPlayers();
5441
5442       /* scroll in two steps of half tile size to make things smoother */
5443       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5444       FlushDisplay();
5445       Delay(wait_delay_value);
5446
5447       /* scroll second step to align at full tile size */
5448       BackToFront();
5449       Delay(wait_delay_value);
5450     }
5451
5452     DrawAllPlayers();
5453     BackToFront();
5454     Delay(wait_delay_value);
5455   }
5456 }
5457
5458 void RelocatePlayer(int jx, int jy, int el_player_raw)
5459 {
5460   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5461   int player_nr = GET_PLAYER_NR(el_player);
5462   struct PlayerInfo *player = &stored_player[player_nr];
5463   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5464   boolean no_delay = (tape.warp_forward);
5465   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5466   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5467   int old_jx = player->jx;
5468   int old_jy = player->jy;
5469   int old_element = Feld[old_jx][old_jy];
5470   int element = Feld[jx][jy];
5471   boolean player_relocated = (old_jx != jx || old_jy != jy);
5472
5473   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5474   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5475   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5476   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5477   int leave_side_horiz = move_dir_horiz;
5478   int leave_side_vert  = move_dir_vert;
5479   int enter_side = enter_side_horiz | enter_side_vert;
5480   int leave_side = leave_side_horiz | leave_side_vert;
5481
5482   if (player->GameOver)         /* do not reanimate dead player */
5483     return;
5484
5485   if (!player_relocated)        /* no need to relocate the player */
5486     return;
5487
5488   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5489   {
5490     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5491     DrawLevelField(jx, jy);
5492   }
5493
5494   if (player->present)
5495   {
5496     while (player->MovPos)
5497     {
5498       ScrollPlayer(player, SCROLL_GO_ON);
5499       ScrollScreen(NULL, SCROLL_GO_ON);
5500
5501       AdvanceFrameAndPlayerCounters(player->index_nr);
5502
5503       DrawPlayer(player);
5504
5505       BackToFront();
5506       Delay(wait_delay_value);
5507     }
5508
5509     DrawPlayer(player);         /* needed here only to cleanup last field */
5510     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5511
5512     player->is_moving = FALSE;
5513   }
5514
5515   if (IS_CUSTOM_ELEMENT(old_element))
5516     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5517                                CE_LEFT_BY_PLAYER,
5518                                player->index_bit, leave_side);
5519
5520   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5521                                       CE_PLAYER_LEAVES_X,
5522                                       player->index_bit, leave_side);
5523
5524   Feld[jx][jy] = el_player;
5525   InitPlayerField(jx, jy, el_player, TRUE);
5526
5527   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5528   {
5529     Feld[jx][jy] = element;
5530     InitField(jx, jy, FALSE);
5531   }
5532
5533   /* only visually relocate centered player */
5534   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5535                      FALSE, level.instant_relocation);
5536
5537   TestIfPlayerTouchesBadThing(jx, jy);
5538   TestIfPlayerTouchesCustomElement(jx, jy);
5539
5540   if (IS_CUSTOM_ELEMENT(element))
5541     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5542                                player->index_bit, enter_side);
5543
5544   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5545                                       player->index_bit, enter_side);
5546 }
5547
5548 void Explode(int ex, int ey, int phase, int mode)
5549 {
5550   int x, y;
5551   int last_phase;
5552   int border_element;
5553
5554   /* !!! eliminate this variable !!! */
5555   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5556
5557   if (game.explosions_delayed)
5558   {
5559     ExplodeField[ex][ey] = mode;
5560     return;
5561   }
5562
5563   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5564   {
5565     int center_element = Feld[ex][ey];
5566     int artwork_element, explosion_element;     /* set these values later */
5567
5568 #if 0
5569     /* --- This is only really needed (and now handled) in "Impact()". --- */
5570     /* do not explode moving elements that left the explode field in time */
5571     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5572         center_element == EL_EMPTY &&
5573         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5574       return;
5575 #endif
5576
5577 #if 0
5578     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5579     if (mode == EX_TYPE_NORMAL ||
5580         mode == EX_TYPE_CENTER ||
5581         mode == EX_TYPE_CROSS)
5582       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5583 #endif
5584
5585     /* remove things displayed in background while burning dynamite */
5586     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5587       Back[ex][ey] = 0;
5588
5589     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5590     {
5591       /* put moving element to center field (and let it explode there) */
5592       center_element = MovingOrBlocked2Element(ex, ey);
5593       RemoveMovingField(ex, ey);
5594       Feld[ex][ey] = center_element;
5595     }
5596
5597     /* now "center_element" is finally determined -- set related values now */
5598     artwork_element = center_element;           /* for custom player artwork */
5599     explosion_element = center_element;         /* for custom player artwork */
5600
5601     if (IS_PLAYER(ex, ey))
5602     {
5603       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5604
5605       artwork_element = stored_player[player_nr].artwork_element;
5606
5607       if (level.use_explosion_element[player_nr])
5608       {
5609         explosion_element = level.explosion_element[player_nr];
5610         artwork_element = explosion_element;
5611       }
5612     }
5613
5614 #if 1
5615     if (mode == EX_TYPE_NORMAL ||
5616         mode == EX_TYPE_CENTER ||
5617         mode == EX_TYPE_CROSS)
5618       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5619 #endif
5620
5621     last_phase = element_info[explosion_element].explosion_delay + 1;
5622
5623     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5624     {
5625       int xx = x - ex + 1;
5626       int yy = y - ey + 1;
5627       int element;
5628
5629       if (!IN_LEV_FIELD(x, y) ||
5630           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5631           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5632         continue;
5633
5634       element = Feld[x][y];
5635
5636       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5637       {
5638         element = MovingOrBlocked2Element(x, y);
5639
5640         if (!IS_EXPLOSION_PROOF(element))
5641           RemoveMovingField(x, y);
5642       }
5643
5644       /* indestructible elements can only explode in center (but not flames) */
5645       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5646                                            mode == EX_TYPE_BORDER)) ||
5647           element == EL_FLAMES)
5648         continue;
5649
5650       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5651          behaviour, for example when touching a yamyam that explodes to rocks
5652          with active deadly shield, a rock is created under the player !!! */
5653       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5654 #if 0
5655       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5656           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5657            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5658 #else
5659       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5660 #endif
5661       {
5662         if (IS_ACTIVE_BOMB(element))
5663         {
5664           /* re-activate things under the bomb like gate or penguin */
5665           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5666           Back[x][y] = 0;
5667         }
5668
5669         continue;
5670       }
5671
5672       /* save walkable background elements while explosion on same tile */
5673       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5674           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5675         Back[x][y] = element;
5676
5677       /* ignite explodable elements reached by other explosion */
5678       if (element == EL_EXPLOSION)
5679         element = Store2[x][y];
5680
5681       if (AmoebaNr[x][y] &&
5682           (element == EL_AMOEBA_FULL ||
5683            element == EL_BD_AMOEBA ||
5684            element == EL_AMOEBA_GROWING))
5685       {
5686         AmoebaCnt[AmoebaNr[x][y]]--;
5687         AmoebaCnt2[AmoebaNr[x][y]]--;
5688       }
5689
5690       RemoveField(x, y);
5691
5692       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5693       {
5694         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5695
5696         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5697
5698         if (PLAYERINFO(ex, ey)->use_murphy)
5699           Store[x][y] = EL_EMPTY;
5700       }
5701
5702       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5703          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5704       else if (ELEM_IS_PLAYER(center_element))
5705         Store[x][y] = EL_EMPTY;
5706       else if (center_element == EL_YAMYAM)
5707         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5708       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5709         Store[x][y] = element_info[center_element].content.e[xx][yy];
5710 #if 1
5711       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5712          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5713          otherwise) -- FIX THIS !!! */
5714       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5715         Store[x][y] = element_info[element].content.e[1][1];
5716 #else
5717       else if (!CAN_EXPLODE(element))
5718         Store[x][y] = element_info[element].content.e[1][1];
5719 #endif
5720       else
5721         Store[x][y] = EL_EMPTY;
5722
5723       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5724           center_element == EL_AMOEBA_TO_DIAMOND)
5725         Store2[x][y] = element;
5726
5727       Feld[x][y] = EL_EXPLOSION;
5728       GfxElement[x][y] = artwork_element;
5729
5730       ExplodePhase[x][y] = 1;
5731       ExplodeDelay[x][y] = last_phase;
5732
5733       Stop[x][y] = TRUE;
5734     }
5735
5736     if (center_element == EL_YAMYAM)
5737       game.yamyam_content_nr =
5738         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5739
5740     return;
5741   }
5742
5743   if (Stop[ex][ey])
5744     return;
5745
5746   x = ex;
5747   y = ey;
5748
5749   if (phase == 1)
5750     GfxFrame[x][y] = 0;         /* restart explosion animation */
5751
5752   last_phase = ExplodeDelay[x][y];
5753
5754   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5755
5756 #ifdef DEBUG
5757
5758   /* activate this even in non-DEBUG version until cause for crash in
5759      getGraphicAnimationFrame() (see below) is found and eliminated */
5760
5761 #endif
5762 #if 1
5763
5764 #if 1
5765   /* this can happen if the player leaves an explosion just in time */
5766   if (GfxElement[x][y] == EL_UNDEFINED)
5767     GfxElement[x][y] = EL_EMPTY;
5768 #else
5769   if (GfxElement[x][y] == EL_UNDEFINED)
5770   {
5771     printf("\n\n");
5772     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5773     printf("Explode(): This should never happen!\n");
5774     printf("\n\n");
5775
5776     GfxElement[x][y] = EL_EMPTY;
5777   }
5778 #endif
5779
5780 #endif
5781
5782   border_element = Store2[x][y];
5783   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5784     border_element = StorePlayer[x][y];
5785
5786   if (phase == element_info[border_element].ignition_delay ||
5787       phase == last_phase)
5788   {
5789     boolean border_explosion = FALSE;
5790
5791     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5792         !PLAYER_EXPLOSION_PROTECTED(x, y))
5793     {
5794       KillPlayerUnlessExplosionProtected(x, y);
5795       border_explosion = TRUE;
5796     }
5797     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5798     {
5799       Feld[x][y] = Store2[x][y];
5800       Store2[x][y] = 0;
5801       Bang(x, y);
5802       border_explosion = TRUE;
5803     }
5804     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5805     {
5806       AmoebeUmwandeln(x, y);
5807       Store2[x][y] = 0;
5808       border_explosion = TRUE;
5809     }
5810
5811     /* if an element just explodes due to another explosion (chain-reaction),
5812        do not immediately end the new explosion when it was the last frame of
5813        the explosion (as it would be done in the following "if"-statement!) */
5814     if (border_explosion && phase == last_phase)
5815       return;
5816   }
5817
5818   if (phase == last_phase)
5819   {
5820     int element;
5821
5822     element = Feld[x][y] = Store[x][y];
5823     Store[x][y] = Store2[x][y] = 0;
5824     GfxElement[x][y] = EL_UNDEFINED;
5825
5826     /* player can escape from explosions and might therefore be still alive */
5827     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5828         element <= EL_PLAYER_IS_EXPLODING_4)
5829     {
5830       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5831       int explosion_element = EL_PLAYER_1 + player_nr;
5832       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5833       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5834
5835       if (level.use_explosion_element[player_nr])
5836         explosion_element = level.explosion_element[player_nr];
5837
5838       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5839                     element_info[explosion_element].content.e[xx][yy]);
5840     }
5841
5842     /* restore probably existing indestructible background element */
5843     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5844       element = Feld[x][y] = Back[x][y];
5845     Back[x][y] = 0;
5846
5847     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5848     GfxDir[x][y] = MV_NONE;
5849     ChangeDelay[x][y] = 0;
5850     ChangePage[x][y] = -1;
5851
5852 #if USE_NEW_CUSTOM_VALUE
5853     CustomValue[x][y] = 0;
5854 #endif
5855
5856     InitField_WithBug2(x, y, FALSE);
5857
5858     TEST_DrawLevelField(x, y);
5859
5860     TestIfElementTouchesCustomElement(x, y);
5861
5862     if (GFX_CRUMBLED(element))
5863       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
5864
5865     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5866       StorePlayer[x][y] = 0;
5867
5868     if (ELEM_IS_PLAYER(element))
5869       RelocatePlayer(x, y, element);
5870   }
5871   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5872   {
5873     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5874     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5875
5876     if (phase == delay)
5877       TEST_DrawLevelFieldCrumbledSand(x, y);
5878
5879     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5880     {
5881       DrawLevelElement(x, y, Back[x][y]);
5882       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5883     }
5884     else if (IS_WALKABLE_UNDER(Back[x][y]))
5885     {
5886       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5887       DrawLevelElementThruMask(x, y, Back[x][y]);
5888     }
5889     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5890       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5891   }
5892 }
5893
5894 void DynaExplode(int ex, int ey)
5895 {
5896   int i, j;
5897   int dynabomb_element = Feld[ex][ey];
5898   int dynabomb_size = 1;
5899   boolean dynabomb_xl = FALSE;
5900   struct PlayerInfo *player;
5901   static int xy[4][2] =
5902   {
5903     { 0, -1 },
5904     { -1, 0 },
5905     { +1, 0 },
5906     { 0, +1 }
5907   };
5908
5909   if (IS_ACTIVE_BOMB(dynabomb_element))
5910   {
5911     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5912     dynabomb_size = player->dynabomb_size;
5913     dynabomb_xl = player->dynabomb_xl;
5914     player->dynabombs_left++;
5915   }
5916
5917   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5918
5919   for (i = 0; i < NUM_DIRECTIONS; i++)
5920   {
5921     for (j = 1; j <= dynabomb_size; j++)
5922     {
5923       int x = ex + j * xy[i][0];
5924       int y = ey + j * xy[i][1];
5925       int element;
5926
5927       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5928         break;
5929
5930       element = Feld[x][y];
5931
5932       /* do not restart explosions of fields with active bombs */
5933       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5934         continue;
5935
5936       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5937
5938       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5939           !IS_DIGGABLE(element) && !dynabomb_xl)
5940         break;
5941     }
5942   }
5943 }
5944
5945 void Bang(int x, int y)
5946 {
5947   int element = MovingOrBlocked2Element(x, y);
5948   int explosion_type = EX_TYPE_NORMAL;
5949
5950   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5951   {
5952     struct PlayerInfo *player = PLAYERINFO(x, y);
5953
5954     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5955                             player->element_nr);
5956
5957     if (level.use_explosion_element[player->index_nr])
5958     {
5959       int explosion_element = level.explosion_element[player->index_nr];
5960
5961       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5962         explosion_type = EX_TYPE_CROSS;
5963       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5964         explosion_type = EX_TYPE_CENTER;
5965     }
5966   }
5967
5968   switch (element)
5969   {
5970     case EL_BUG:
5971     case EL_SPACESHIP:
5972     case EL_BD_BUTTERFLY:
5973     case EL_BD_FIREFLY:
5974     case EL_YAMYAM:
5975     case EL_DARK_YAMYAM:
5976     case EL_ROBOT:
5977     case EL_PACMAN:
5978     case EL_MOLE:
5979       RaiseScoreElement(element);
5980       break;
5981
5982     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5983     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5984     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5985     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5986     case EL_DYNABOMB_INCREASE_NUMBER:
5987     case EL_DYNABOMB_INCREASE_SIZE:
5988     case EL_DYNABOMB_INCREASE_POWER:
5989       explosion_type = EX_TYPE_DYNA;
5990       break;
5991
5992     case EL_DC_LANDMINE:
5993 #if 0
5994     case EL_EM_EXIT_OPEN:
5995     case EL_EM_STEEL_EXIT_OPEN:
5996 #endif
5997       explosion_type = EX_TYPE_CENTER;
5998       break;
5999
6000     case EL_PENGUIN:
6001     case EL_LAMP:
6002     case EL_LAMP_ACTIVE:
6003     case EL_AMOEBA_TO_DIAMOND:
6004       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6005         explosion_type = EX_TYPE_CENTER;
6006       break;
6007
6008     default:
6009       if (element_info[element].explosion_type == EXPLODES_CROSS)
6010         explosion_type = EX_TYPE_CROSS;
6011       else if (element_info[element].explosion_type == EXPLODES_1X1)
6012         explosion_type = EX_TYPE_CENTER;
6013       break;
6014   }
6015
6016   if (explosion_type == EX_TYPE_DYNA)
6017     DynaExplode(x, y);
6018   else
6019     Explode(x, y, EX_PHASE_START, explosion_type);
6020
6021   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6022 }
6023
6024 void SplashAcid(int x, int y)
6025 {
6026   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6027       (!IN_LEV_FIELD(x - 1, y - 2) ||
6028        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6029     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6030
6031   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6032       (!IN_LEV_FIELD(x + 1, y - 2) ||
6033        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6034     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6035
6036   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6037 }
6038
6039 static void InitBeltMovement()
6040 {
6041   static int belt_base_element[4] =
6042   {
6043     EL_CONVEYOR_BELT_1_LEFT,
6044     EL_CONVEYOR_BELT_2_LEFT,
6045     EL_CONVEYOR_BELT_3_LEFT,
6046     EL_CONVEYOR_BELT_4_LEFT
6047   };
6048   static int belt_base_active_element[4] =
6049   {
6050     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6051     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6052     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6053     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6054   };
6055
6056   int x, y, i, j;
6057
6058   /* set frame order for belt animation graphic according to belt direction */
6059   for (i = 0; i < NUM_BELTS; i++)
6060   {
6061     int belt_nr = i;
6062
6063     for (j = 0; j < NUM_BELT_PARTS; j++)
6064     {
6065       int element = belt_base_active_element[belt_nr] + j;
6066       int graphic_1 = el2img(element);
6067       int graphic_2 = el2panelimg(element);
6068
6069       if (game.belt_dir[i] == MV_LEFT)
6070       {
6071         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6072         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6073       }
6074       else
6075       {
6076         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6077         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6078       }
6079     }
6080   }
6081
6082   SCAN_PLAYFIELD(x, y)
6083   {
6084     int element = Feld[x][y];
6085
6086     for (i = 0; i < NUM_BELTS; i++)
6087     {
6088       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6089       {
6090         int e_belt_nr = getBeltNrFromBeltElement(element);
6091         int belt_nr = i;
6092
6093         if (e_belt_nr == belt_nr)
6094         {
6095           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6096
6097           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6098         }
6099       }
6100     }
6101   }
6102 }
6103
6104 static void ToggleBeltSwitch(int x, int y)
6105 {
6106   static int belt_base_element[4] =
6107   {
6108     EL_CONVEYOR_BELT_1_LEFT,
6109     EL_CONVEYOR_BELT_2_LEFT,
6110     EL_CONVEYOR_BELT_3_LEFT,
6111     EL_CONVEYOR_BELT_4_LEFT
6112   };
6113   static int belt_base_active_element[4] =
6114   {
6115     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6116     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6117     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6118     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6119   };
6120   static int belt_base_switch_element[4] =
6121   {
6122     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6123     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6124     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6125     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6126   };
6127   static int belt_move_dir[4] =
6128   {
6129     MV_LEFT,
6130     MV_NONE,
6131     MV_RIGHT,
6132     MV_NONE,
6133   };
6134
6135   int element = Feld[x][y];
6136   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6137   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6138   int belt_dir = belt_move_dir[belt_dir_nr];
6139   int xx, yy, i;
6140
6141   if (!IS_BELT_SWITCH(element))
6142     return;
6143
6144   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6145   game.belt_dir[belt_nr] = belt_dir;
6146
6147   if (belt_dir_nr == 3)
6148     belt_dir_nr = 1;
6149
6150   /* set frame order for belt animation graphic according to belt direction */
6151   for (i = 0; i < NUM_BELT_PARTS; i++)
6152   {
6153     int element = belt_base_active_element[belt_nr] + i;
6154     int graphic_1 = el2img(element);
6155     int graphic_2 = el2panelimg(element);
6156
6157     if (belt_dir == MV_LEFT)
6158     {
6159       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6160       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6161     }
6162     else
6163     {
6164       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6165       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6166     }
6167   }
6168
6169   SCAN_PLAYFIELD(xx, yy)
6170   {
6171     int element = Feld[xx][yy];
6172
6173     if (IS_BELT_SWITCH(element))
6174     {
6175       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6176
6177       if (e_belt_nr == belt_nr)
6178       {
6179         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6180         TEST_DrawLevelField(xx, yy);
6181       }
6182     }
6183     else if (IS_BELT(element) && belt_dir != MV_NONE)
6184     {
6185       int e_belt_nr = getBeltNrFromBeltElement(element);
6186
6187       if (e_belt_nr == belt_nr)
6188       {
6189         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6190
6191         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6192         TEST_DrawLevelField(xx, yy);
6193       }
6194     }
6195     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6196     {
6197       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6198
6199       if (e_belt_nr == belt_nr)
6200       {
6201         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6202
6203         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6204         TEST_DrawLevelField(xx, yy);
6205       }
6206     }
6207   }
6208 }
6209
6210 static void ToggleSwitchgateSwitch(int x, int y)
6211 {
6212   int xx, yy;
6213
6214   game.switchgate_pos = !game.switchgate_pos;
6215
6216   SCAN_PLAYFIELD(xx, yy)
6217   {
6218     int element = Feld[xx][yy];
6219
6220 #if !USE_BOTH_SWITCHGATE_SWITCHES
6221     if (element == EL_SWITCHGATE_SWITCH_UP ||
6222         element == EL_SWITCHGATE_SWITCH_DOWN)
6223     {
6224       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6225       TEST_DrawLevelField(xx, yy);
6226     }
6227     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6228              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6229     {
6230       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6231       TEST_DrawLevelField(xx, yy);
6232     }
6233 #else
6234     if (element == EL_SWITCHGATE_SWITCH_UP)
6235     {
6236       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6237       TEST_DrawLevelField(xx, yy);
6238     }
6239     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6240     {
6241       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6242       TEST_DrawLevelField(xx, yy);
6243     }
6244     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6245     {
6246       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6247       TEST_DrawLevelField(xx, yy);
6248     }
6249     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6250     {
6251       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6252       TEST_DrawLevelField(xx, yy);
6253     }
6254 #endif
6255     else if (element == EL_SWITCHGATE_OPEN ||
6256              element == EL_SWITCHGATE_OPENING)
6257     {
6258       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6259
6260       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6261     }
6262     else if (element == EL_SWITCHGATE_CLOSED ||
6263              element == EL_SWITCHGATE_CLOSING)
6264     {
6265       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6266
6267       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6268     }
6269   }
6270 }
6271
6272 static int getInvisibleActiveFromInvisibleElement(int element)
6273 {
6274   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6275           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6276           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6277           element);
6278 }
6279
6280 static int getInvisibleFromInvisibleActiveElement(int element)
6281 {
6282   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6283           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6284           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6285           element);
6286 }
6287
6288 static void RedrawAllLightSwitchesAndInvisibleElements()
6289 {
6290   int x, y;
6291
6292   SCAN_PLAYFIELD(x, y)
6293   {
6294     int element = Feld[x][y];
6295
6296     if (element == EL_LIGHT_SWITCH &&
6297         game.light_time_left > 0)
6298     {
6299       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6300       TEST_DrawLevelField(x, y);
6301     }
6302     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6303              game.light_time_left == 0)
6304     {
6305       Feld[x][y] = EL_LIGHT_SWITCH;
6306       TEST_DrawLevelField(x, y);
6307     }
6308     else if (element == EL_EMC_DRIPPER &&
6309              game.light_time_left > 0)
6310     {
6311       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6312       TEST_DrawLevelField(x, y);
6313     }
6314     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6315              game.light_time_left == 0)
6316     {
6317       Feld[x][y] = EL_EMC_DRIPPER;
6318       TEST_DrawLevelField(x, y);
6319     }
6320     else if (element == EL_INVISIBLE_STEELWALL ||
6321              element == EL_INVISIBLE_WALL ||
6322              element == EL_INVISIBLE_SAND)
6323     {
6324       if (game.light_time_left > 0)
6325         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6326
6327       TEST_DrawLevelField(x, y);
6328
6329       /* uncrumble neighbour fields, if needed */
6330       if (element == EL_INVISIBLE_SAND)
6331         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6332     }
6333     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6334              element == EL_INVISIBLE_WALL_ACTIVE ||
6335              element == EL_INVISIBLE_SAND_ACTIVE)
6336     {
6337       if (game.light_time_left == 0)
6338         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6339
6340       TEST_DrawLevelField(x, y);
6341
6342       /* re-crumble neighbour fields, if needed */
6343       if (element == EL_INVISIBLE_SAND)
6344         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6345     }
6346   }
6347 }
6348
6349 static void RedrawAllInvisibleElementsForLenses()
6350 {
6351   int x, y;
6352
6353   SCAN_PLAYFIELD(x, y)
6354   {
6355     int element = Feld[x][y];
6356
6357     if (element == EL_EMC_DRIPPER &&
6358         game.lenses_time_left > 0)
6359     {
6360       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6361       TEST_DrawLevelField(x, y);
6362     }
6363     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6364              game.lenses_time_left == 0)
6365     {
6366       Feld[x][y] = EL_EMC_DRIPPER;
6367       TEST_DrawLevelField(x, y);
6368     }
6369     else if (element == EL_INVISIBLE_STEELWALL ||
6370              element == EL_INVISIBLE_WALL ||
6371              element == EL_INVISIBLE_SAND)
6372     {
6373       if (game.lenses_time_left > 0)
6374         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6375
6376       TEST_DrawLevelField(x, y);
6377
6378       /* uncrumble neighbour fields, if needed */
6379       if (element == EL_INVISIBLE_SAND)
6380         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6381     }
6382     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6383              element == EL_INVISIBLE_WALL_ACTIVE ||
6384              element == EL_INVISIBLE_SAND_ACTIVE)
6385     {
6386       if (game.lenses_time_left == 0)
6387         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6388
6389       TEST_DrawLevelField(x, y);
6390
6391       /* re-crumble neighbour fields, if needed */
6392       if (element == EL_INVISIBLE_SAND)
6393         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6394     }
6395   }
6396 }
6397
6398 static void RedrawAllInvisibleElementsForMagnifier()
6399 {
6400   int x, y;
6401
6402   SCAN_PLAYFIELD(x, y)
6403   {
6404     int element = Feld[x][y];
6405
6406     if (element == EL_EMC_FAKE_GRASS &&
6407         game.magnify_time_left > 0)
6408     {
6409       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6410       TEST_DrawLevelField(x, y);
6411     }
6412     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6413              game.magnify_time_left == 0)
6414     {
6415       Feld[x][y] = EL_EMC_FAKE_GRASS;
6416       TEST_DrawLevelField(x, y);
6417     }
6418     else if (IS_GATE_GRAY(element) &&
6419              game.magnify_time_left > 0)
6420     {
6421       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6422                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6423                     IS_EM_GATE_GRAY(element) ?
6424                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6425                     IS_EMC_GATE_GRAY(element) ?
6426                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6427                     element);
6428       TEST_DrawLevelField(x, y);
6429     }
6430     else if (IS_GATE_GRAY_ACTIVE(element) &&
6431              game.magnify_time_left == 0)
6432     {
6433       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6434                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6435                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6436                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6437                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6438                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6439                     element);
6440       TEST_DrawLevelField(x, y);
6441     }
6442   }
6443 }
6444
6445 static void ToggleLightSwitch(int x, int y)
6446 {
6447   int element = Feld[x][y];
6448
6449   game.light_time_left =
6450     (element == EL_LIGHT_SWITCH ?
6451      level.time_light * FRAMES_PER_SECOND : 0);
6452
6453   RedrawAllLightSwitchesAndInvisibleElements();
6454 }
6455
6456 static void ActivateTimegateSwitch(int x, int y)
6457 {
6458   int xx, yy;
6459
6460   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6461
6462   SCAN_PLAYFIELD(xx, yy)
6463   {
6464     int element = Feld[xx][yy];
6465
6466     if (element == EL_TIMEGATE_CLOSED ||
6467         element == EL_TIMEGATE_CLOSING)
6468     {
6469       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6470       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6471     }
6472
6473     /*
6474     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6475     {
6476       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6477       TEST_DrawLevelField(xx, yy);
6478     }
6479     */
6480
6481   }
6482
6483 #if 1
6484   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6485                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6486 #else
6487   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6488 #endif
6489 }
6490
6491 void Impact(int x, int y)
6492 {
6493   boolean last_line = (y == lev_fieldy - 1);
6494   boolean object_hit = FALSE;
6495   boolean impact = (last_line || object_hit);
6496   int element = Feld[x][y];
6497   int smashed = EL_STEELWALL;
6498
6499   if (!last_line)       /* check if element below was hit */
6500   {
6501     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6502       return;
6503
6504     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6505                                          MovDir[x][y + 1] != MV_DOWN ||
6506                                          MovPos[x][y + 1] <= TILEY / 2));
6507
6508     /* do not smash moving elements that left the smashed field in time */
6509     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6510         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6511       object_hit = FALSE;
6512
6513 #if USE_QUICKSAND_IMPACT_BUGFIX
6514     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6515     {
6516       RemoveMovingField(x, y + 1);
6517       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6518       Feld[x][y + 2] = EL_ROCK;
6519       TEST_DrawLevelField(x, y + 2);
6520
6521       object_hit = TRUE;
6522     }
6523
6524     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6525     {
6526       RemoveMovingField(x, y + 1);
6527       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6528       Feld[x][y + 2] = EL_ROCK;
6529       TEST_DrawLevelField(x, y + 2);
6530
6531       object_hit = TRUE;
6532     }
6533 #endif
6534
6535     if (object_hit)
6536       smashed = MovingOrBlocked2Element(x, y + 1);
6537
6538     impact = (last_line || object_hit);
6539   }
6540
6541   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6542   {
6543     SplashAcid(x, y + 1);
6544     return;
6545   }
6546
6547   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6548   /* only reset graphic animation if graphic really changes after impact */
6549   if (impact &&
6550       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6551   {
6552     ResetGfxAnimation(x, y);
6553     TEST_DrawLevelField(x, y);
6554   }
6555
6556   if (impact && CAN_EXPLODE_IMPACT(element))
6557   {
6558     Bang(x, y);
6559     return;
6560   }
6561   else if (impact && element == EL_PEARL &&
6562            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6563   {
6564     ResetGfxAnimation(x, y);
6565
6566     Feld[x][y] = EL_PEARL_BREAKING;
6567     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6568     return;
6569   }
6570   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6571   {
6572     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6573
6574     return;
6575   }
6576
6577   if (impact && element == EL_AMOEBA_DROP)
6578   {
6579     if (object_hit && IS_PLAYER(x, y + 1))
6580       KillPlayerUnlessEnemyProtected(x, y + 1);
6581     else if (object_hit && smashed == EL_PENGUIN)
6582       Bang(x, y + 1);
6583     else
6584     {
6585       Feld[x][y] = EL_AMOEBA_GROWING;
6586       Store[x][y] = EL_AMOEBA_WET;
6587
6588       ResetRandomAnimationValue(x, y);
6589     }
6590     return;
6591   }
6592
6593   if (object_hit)               /* check which object was hit */
6594   {
6595     if ((CAN_PASS_MAGIC_WALL(element) && 
6596          (smashed == EL_MAGIC_WALL ||
6597           smashed == EL_BD_MAGIC_WALL)) ||
6598         (CAN_PASS_DC_MAGIC_WALL(element) &&
6599          smashed == EL_DC_MAGIC_WALL))
6600     {
6601       int xx, yy;
6602       int activated_magic_wall =
6603         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6604          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6605          EL_DC_MAGIC_WALL_ACTIVE);
6606
6607       /* activate magic wall / mill */
6608       SCAN_PLAYFIELD(xx, yy)
6609       {
6610         if (Feld[xx][yy] == smashed)
6611           Feld[xx][yy] = activated_magic_wall;
6612       }
6613
6614       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6615       game.magic_wall_active = TRUE;
6616
6617       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6618                             SND_MAGIC_WALL_ACTIVATING :
6619                             smashed == EL_BD_MAGIC_WALL ?
6620                             SND_BD_MAGIC_WALL_ACTIVATING :
6621                             SND_DC_MAGIC_WALL_ACTIVATING));
6622     }
6623
6624     if (IS_PLAYER(x, y + 1))
6625     {
6626       if (CAN_SMASH_PLAYER(element))
6627       {
6628         KillPlayerUnlessEnemyProtected(x, y + 1);
6629         return;
6630       }
6631     }
6632     else if (smashed == EL_PENGUIN)
6633     {
6634       if (CAN_SMASH_PLAYER(element))
6635       {
6636         Bang(x, y + 1);
6637         return;
6638       }
6639     }
6640     else if (element == EL_BD_DIAMOND)
6641     {
6642       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6643       {
6644         Bang(x, y + 1);
6645         return;
6646       }
6647     }
6648     else if (((element == EL_SP_INFOTRON ||
6649                element == EL_SP_ZONK) &&
6650               (smashed == EL_SP_SNIKSNAK ||
6651                smashed == EL_SP_ELECTRON ||
6652                smashed == EL_SP_DISK_ORANGE)) ||
6653              (element == EL_SP_INFOTRON &&
6654               smashed == EL_SP_DISK_YELLOW))
6655     {
6656       Bang(x, y + 1);
6657       return;
6658     }
6659     else if (CAN_SMASH_EVERYTHING(element))
6660     {
6661       if (IS_CLASSIC_ENEMY(smashed) ||
6662           CAN_EXPLODE_SMASHED(smashed))
6663       {
6664         Bang(x, y + 1);
6665         return;
6666       }
6667       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6668       {
6669         if (smashed == EL_LAMP ||
6670             smashed == EL_LAMP_ACTIVE)
6671         {
6672           Bang(x, y + 1);
6673           return;
6674         }
6675         else if (smashed == EL_NUT)
6676         {
6677           Feld[x][y + 1] = EL_NUT_BREAKING;
6678           PlayLevelSound(x, y, SND_NUT_BREAKING);
6679           RaiseScoreElement(EL_NUT);
6680           return;
6681         }
6682         else if (smashed == EL_PEARL)
6683         {
6684           ResetGfxAnimation(x, y);
6685
6686           Feld[x][y + 1] = EL_PEARL_BREAKING;
6687           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6688           return;
6689         }
6690         else if (smashed == EL_DIAMOND)
6691         {
6692           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6693           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6694           return;
6695         }
6696         else if (IS_BELT_SWITCH(smashed))
6697         {
6698           ToggleBeltSwitch(x, y + 1);
6699         }
6700         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6701                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6702                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6703                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6704         {
6705           ToggleSwitchgateSwitch(x, y + 1);
6706         }
6707         else if (smashed == EL_LIGHT_SWITCH ||
6708                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6709         {
6710           ToggleLightSwitch(x, y + 1);
6711         }
6712         else
6713         {
6714 #if 0
6715           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6716 #endif
6717
6718           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6719
6720           CheckElementChangeBySide(x, y + 1, smashed, element,
6721                                    CE_SWITCHED, CH_SIDE_TOP);
6722           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6723                                             CH_SIDE_TOP);
6724         }
6725       }
6726       else
6727       {
6728         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6729       }
6730     }
6731   }
6732
6733   /* play sound of magic wall / mill */
6734   if (!last_line &&
6735       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6736        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6737        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6738   {
6739     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6740       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6741     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6742       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6743     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6744       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6745
6746     return;
6747   }
6748
6749   /* play sound of object that hits the ground */
6750   if (last_line || object_hit)
6751     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6752 }
6753
6754 inline static void TurnRoundExt(int x, int y)
6755 {
6756   static struct
6757   {
6758     int dx, dy;
6759   } move_xy[] =
6760   {
6761     {  0,  0 },
6762     { -1,  0 },
6763     { +1,  0 },
6764     {  0,  0 },
6765     {  0, -1 },
6766     {  0,  0 }, { 0, 0 }, { 0, 0 },
6767     {  0, +1 }
6768   };
6769   static struct
6770   {
6771     int left, right, back;
6772   } turn[] =
6773   {
6774     { 0,        0,              0        },
6775     { MV_DOWN,  MV_UP,          MV_RIGHT },
6776     { MV_UP,    MV_DOWN,        MV_LEFT  },
6777     { 0,        0,              0        },
6778     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6779     { 0,        0,              0        },
6780     { 0,        0,              0        },
6781     { 0,        0,              0        },
6782     { MV_RIGHT, MV_LEFT,        MV_UP    }
6783   };
6784
6785   int element = Feld[x][y];
6786   int move_pattern = element_info[element].move_pattern;
6787
6788   int old_move_dir = MovDir[x][y];
6789   int left_dir  = turn[old_move_dir].left;
6790   int right_dir = turn[old_move_dir].right;
6791   int back_dir  = turn[old_move_dir].back;
6792
6793   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6794   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6795   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6796   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6797
6798   int left_x  = x + left_dx,  left_y  = y + left_dy;
6799   int right_x = x + right_dx, right_y = y + right_dy;
6800   int move_x  = x + move_dx,  move_y  = y + move_dy;
6801
6802   int xx, yy;
6803
6804   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6805   {
6806     TestIfBadThingTouchesOtherBadThing(x, y);
6807
6808     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6809       MovDir[x][y] = right_dir;
6810     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6811       MovDir[x][y] = left_dir;
6812
6813     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6814       MovDelay[x][y] = 9;
6815     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6816       MovDelay[x][y] = 1;
6817   }
6818   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6819   {
6820     TestIfBadThingTouchesOtherBadThing(x, y);
6821
6822     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6823       MovDir[x][y] = left_dir;
6824     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6825       MovDir[x][y] = right_dir;
6826
6827     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6828       MovDelay[x][y] = 9;
6829     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6830       MovDelay[x][y] = 1;
6831   }
6832   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6833   {
6834     TestIfBadThingTouchesOtherBadThing(x, y);
6835
6836     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6837       MovDir[x][y] = left_dir;
6838     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6839       MovDir[x][y] = right_dir;
6840
6841     if (MovDir[x][y] != old_move_dir)
6842       MovDelay[x][y] = 9;
6843   }
6844   else if (element == EL_YAMYAM)
6845   {
6846     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6847     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6848
6849     if (can_turn_left && can_turn_right)
6850       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6851     else if (can_turn_left)
6852       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6853     else if (can_turn_right)
6854       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6855     else
6856       MovDir[x][y] = back_dir;
6857
6858     MovDelay[x][y] = 16 + 16 * RND(3);
6859   }
6860   else if (element == EL_DARK_YAMYAM)
6861   {
6862     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6863                                                          left_x, left_y);
6864     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6865                                                          right_x, right_y);
6866
6867     if (can_turn_left && can_turn_right)
6868       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6869     else if (can_turn_left)
6870       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6871     else if (can_turn_right)
6872       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6873     else
6874       MovDir[x][y] = back_dir;
6875
6876     MovDelay[x][y] = 16 + 16 * RND(3);
6877   }
6878   else if (element == EL_PACMAN)
6879   {
6880     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6881     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6882
6883     if (can_turn_left && can_turn_right)
6884       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6885     else if (can_turn_left)
6886       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6887     else if (can_turn_right)
6888       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6889     else
6890       MovDir[x][y] = back_dir;
6891
6892     MovDelay[x][y] = 6 + RND(40);
6893   }
6894   else if (element == EL_PIG)
6895   {
6896     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6897     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6898     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6899     boolean should_turn_left, should_turn_right, should_move_on;
6900     int rnd_value = 24;
6901     int rnd = RND(rnd_value);
6902
6903     should_turn_left = (can_turn_left &&
6904                         (!can_move_on ||
6905                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6906                                                    y + back_dy + left_dy)));
6907     should_turn_right = (can_turn_right &&
6908                          (!can_move_on ||
6909                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6910                                                     y + back_dy + right_dy)));
6911     should_move_on = (can_move_on &&
6912                       (!can_turn_left ||
6913                        !can_turn_right ||
6914                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6915                                                  y + move_dy + left_dy) ||
6916                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6917                                                  y + move_dy + right_dy)));
6918
6919     if (should_turn_left || should_turn_right || should_move_on)
6920     {
6921       if (should_turn_left && should_turn_right && should_move_on)
6922         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6923                         rnd < 2 * rnd_value / 3 ? right_dir :
6924                         old_move_dir);
6925       else if (should_turn_left && should_turn_right)
6926         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6927       else if (should_turn_left && should_move_on)
6928         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6929       else if (should_turn_right && should_move_on)
6930         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6931       else if (should_turn_left)
6932         MovDir[x][y] = left_dir;
6933       else if (should_turn_right)
6934         MovDir[x][y] = right_dir;
6935       else if (should_move_on)
6936         MovDir[x][y] = old_move_dir;
6937     }
6938     else if (can_move_on && rnd > rnd_value / 8)
6939       MovDir[x][y] = old_move_dir;
6940     else if (can_turn_left && can_turn_right)
6941       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6942     else if (can_turn_left && rnd > rnd_value / 8)
6943       MovDir[x][y] = left_dir;
6944     else if (can_turn_right && rnd > rnd_value/8)
6945       MovDir[x][y] = right_dir;
6946     else
6947       MovDir[x][y] = back_dir;
6948
6949     xx = x + move_xy[MovDir[x][y]].dx;
6950     yy = y + move_xy[MovDir[x][y]].dy;
6951
6952     if (!IN_LEV_FIELD(xx, yy) ||
6953         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6954       MovDir[x][y] = old_move_dir;
6955
6956     MovDelay[x][y] = 0;
6957   }
6958   else if (element == EL_DRAGON)
6959   {
6960     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6961     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6962     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6963     int rnd_value = 24;
6964     int rnd = RND(rnd_value);
6965
6966     if (can_move_on && rnd > rnd_value / 8)
6967       MovDir[x][y] = old_move_dir;
6968     else if (can_turn_left && can_turn_right)
6969       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6970     else if (can_turn_left && rnd > rnd_value / 8)
6971       MovDir[x][y] = left_dir;
6972     else if (can_turn_right && rnd > rnd_value / 8)
6973       MovDir[x][y] = right_dir;
6974     else
6975       MovDir[x][y] = back_dir;
6976
6977     xx = x + move_xy[MovDir[x][y]].dx;
6978     yy = y + move_xy[MovDir[x][y]].dy;
6979
6980     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6981       MovDir[x][y] = old_move_dir;
6982
6983     MovDelay[x][y] = 0;
6984   }
6985   else if (element == EL_MOLE)
6986   {
6987     boolean can_move_on =
6988       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6989                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6990                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6991     if (!can_move_on)
6992     {
6993       boolean can_turn_left =
6994         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6995                               IS_AMOEBOID(Feld[left_x][left_y])));
6996
6997       boolean can_turn_right =
6998         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6999                               IS_AMOEBOID(Feld[right_x][right_y])));
7000
7001       if (can_turn_left && can_turn_right)
7002         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7003       else if (can_turn_left)
7004         MovDir[x][y] = left_dir;
7005       else
7006         MovDir[x][y] = right_dir;
7007     }
7008
7009     if (MovDir[x][y] != old_move_dir)
7010       MovDelay[x][y] = 9;
7011   }
7012   else if (element == EL_BALLOON)
7013   {
7014     MovDir[x][y] = game.wind_direction;
7015     MovDelay[x][y] = 0;
7016   }
7017   else if (element == EL_SPRING)
7018   {
7019 #if USE_NEW_SPRING_BUMPER
7020     if (MovDir[x][y] & MV_HORIZONTAL)
7021     {
7022       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7023           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7024       {
7025         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7026         ResetGfxAnimation(move_x, move_y);
7027         TEST_DrawLevelField(move_x, move_y);
7028
7029         MovDir[x][y] = back_dir;
7030       }
7031       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7032                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7033         MovDir[x][y] = MV_NONE;
7034     }
7035 #else
7036     if (MovDir[x][y] & MV_HORIZONTAL &&
7037         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7038          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7039       MovDir[x][y] = MV_NONE;
7040 #endif
7041
7042     MovDelay[x][y] = 0;
7043   }
7044   else if (element == EL_ROBOT ||
7045            element == EL_SATELLITE ||
7046            element == EL_PENGUIN ||
7047            element == EL_EMC_ANDROID)
7048   {
7049     int attr_x = -1, attr_y = -1;
7050
7051     if (AllPlayersGone)
7052     {
7053       attr_x = ExitX;
7054       attr_y = ExitY;
7055     }
7056     else
7057     {
7058       int i;
7059
7060       for (i = 0; i < MAX_PLAYERS; i++)
7061       {
7062         struct PlayerInfo *player = &stored_player[i];
7063         int jx = player->jx, jy = player->jy;
7064
7065         if (!player->active)
7066           continue;
7067
7068         if (attr_x == -1 ||
7069             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7070         {
7071           attr_x = jx;
7072           attr_y = jy;
7073         }
7074       }
7075     }
7076
7077     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7078         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7079          game.engine_version < VERSION_IDENT(3,1,0,0)))
7080     {
7081       attr_x = ZX;
7082       attr_y = ZY;
7083     }
7084
7085     if (element == EL_PENGUIN)
7086     {
7087       int i;
7088       static int xy[4][2] =
7089       {
7090         { 0, -1 },
7091         { -1, 0 },
7092         { +1, 0 },
7093         { 0, +1 }
7094       };
7095
7096       for (i = 0; i < NUM_DIRECTIONS; i++)
7097       {
7098         int ex = x + xy[i][0];
7099         int ey = y + xy[i][1];
7100
7101         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7102                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7103                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7104                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7105         {
7106           attr_x = ex;
7107           attr_y = ey;
7108           break;
7109         }
7110       }
7111     }
7112
7113     MovDir[x][y] = MV_NONE;
7114     if (attr_x < x)
7115       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7116     else if (attr_x > x)
7117       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7118     if (attr_y < y)
7119       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7120     else if (attr_y > y)
7121       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7122
7123     if (element == EL_ROBOT)
7124     {
7125       int newx, newy;
7126
7127       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7128         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7129       Moving2Blocked(x, y, &newx, &newy);
7130
7131       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7132         MovDelay[x][y] = 8 + 8 * !RND(3);
7133       else
7134         MovDelay[x][y] = 16;
7135     }
7136     else if (element == EL_PENGUIN)
7137     {
7138       int newx, newy;
7139
7140       MovDelay[x][y] = 1;
7141
7142       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7143       {
7144         boolean first_horiz = RND(2);
7145         int new_move_dir = MovDir[x][y];
7146
7147         MovDir[x][y] =
7148           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7149         Moving2Blocked(x, y, &newx, &newy);
7150
7151         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7152           return;
7153
7154         MovDir[x][y] =
7155           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7156         Moving2Blocked(x, y, &newx, &newy);
7157
7158         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7159           return;
7160
7161         MovDir[x][y] = old_move_dir;
7162         return;
7163       }
7164     }
7165     else if (element == EL_SATELLITE)
7166     {
7167       int newx, newy;
7168
7169       MovDelay[x][y] = 1;
7170
7171       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7172       {
7173         boolean first_horiz = RND(2);
7174         int new_move_dir = MovDir[x][y];
7175
7176         MovDir[x][y] =
7177           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7178         Moving2Blocked(x, y, &newx, &newy);
7179
7180         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7181           return;
7182
7183         MovDir[x][y] =
7184           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7185         Moving2Blocked(x, y, &newx, &newy);
7186
7187         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7188           return;
7189
7190         MovDir[x][y] = old_move_dir;
7191         return;
7192       }
7193     }
7194     else if (element == EL_EMC_ANDROID)
7195     {
7196       static int check_pos[16] =
7197       {
7198         -1,             /*  0 => (invalid)          */
7199         7,              /*  1 => MV_LEFT            */
7200         3,              /*  2 => MV_RIGHT           */
7201         -1,             /*  3 => (invalid)          */
7202         1,              /*  4 =>            MV_UP   */
7203         0,              /*  5 => MV_LEFT  | MV_UP   */
7204         2,              /*  6 => MV_RIGHT | MV_UP   */
7205         -1,             /*  7 => (invalid)          */
7206         5,              /*  8 =>            MV_DOWN */
7207         6,              /*  9 => MV_LEFT  | MV_DOWN */
7208         4,              /* 10 => MV_RIGHT | MV_DOWN */
7209         -1,             /* 11 => (invalid)          */
7210         -1,             /* 12 => (invalid)          */
7211         -1,             /* 13 => (invalid)          */
7212         -1,             /* 14 => (invalid)          */
7213         -1,             /* 15 => (invalid)          */
7214       };
7215       static struct
7216       {
7217         int dx, dy;
7218         int dir;
7219       } check_xy[8] =
7220       {
7221         { -1, -1,       MV_LEFT  | MV_UP   },
7222         {  0, -1,                  MV_UP   },
7223         { +1, -1,       MV_RIGHT | MV_UP   },
7224         { +1,  0,       MV_RIGHT           },
7225         { +1, +1,       MV_RIGHT | MV_DOWN },
7226         {  0, +1,                  MV_DOWN },
7227         { -1, +1,       MV_LEFT  | MV_DOWN },
7228         { -1,  0,       MV_LEFT            },
7229       };
7230       int start_pos, check_order;
7231       boolean can_clone = FALSE;
7232       int i;
7233
7234       /* check if there is any free field around current position */
7235       for (i = 0; i < 8; i++)
7236       {
7237         int newx = x + check_xy[i].dx;
7238         int newy = y + check_xy[i].dy;
7239
7240         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7241         {
7242           can_clone = TRUE;
7243
7244           break;
7245         }
7246       }
7247
7248       if (can_clone)            /* randomly find an element to clone */
7249       {
7250         can_clone = FALSE;
7251
7252         start_pos = check_pos[RND(8)];
7253         check_order = (RND(2) ? -1 : +1);
7254
7255         for (i = 0; i < 8; i++)
7256         {
7257           int pos_raw = start_pos + i * check_order;
7258           int pos = (pos_raw + 8) % 8;
7259           int newx = x + check_xy[pos].dx;
7260           int newy = y + check_xy[pos].dy;
7261
7262           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7263           {
7264             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7265             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7266
7267             Store[x][y] = Feld[newx][newy];
7268
7269             can_clone = TRUE;
7270
7271             break;
7272           }
7273         }
7274       }
7275
7276       if (can_clone)            /* randomly find a direction to move */
7277       {
7278         can_clone = FALSE;
7279
7280         start_pos = check_pos[RND(8)];
7281         check_order = (RND(2) ? -1 : +1);
7282
7283         for (i = 0; i < 8; i++)
7284         {
7285           int pos_raw = start_pos + i * check_order;
7286           int pos = (pos_raw + 8) % 8;
7287           int newx = x + check_xy[pos].dx;
7288           int newy = y + check_xy[pos].dy;
7289           int new_move_dir = check_xy[pos].dir;
7290
7291           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7292           {
7293             MovDir[x][y] = new_move_dir;
7294             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7295
7296             can_clone = TRUE;
7297
7298             break;
7299           }
7300         }
7301       }
7302
7303       if (can_clone)            /* cloning and moving successful */
7304         return;
7305
7306       /* cannot clone -- try to move towards player */
7307
7308       start_pos = check_pos[MovDir[x][y] & 0x0f];
7309       check_order = (RND(2) ? -1 : +1);
7310
7311       for (i = 0; i < 3; i++)
7312       {
7313         /* first check start_pos, then previous/next or (next/previous) pos */
7314         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7315         int pos = (pos_raw + 8) % 8;
7316         int newx = x + check_xy[pos].dx;
7317         int newy = y + check_xy[pos].dy;
7318         int new_move_dir = check_xy[pos].dir;
7319
7320         if (IS_PLAYER(newx, newy))
7321           break;
7322
7323         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7324         {
7325           MovDir[x][y] = new_move_dir;
7326           MovDelay[x][y] = level.android_move_time * 8 + 1;
7327
7328           break;
7329         }
7330       }
7331     }
7332   }
7333   else if (move_pattern == MV_TURNING_LEFT ||
7334            move_pattern == MV_TURNING_RIGHT ||
7335            move_pattern == MV_TURNING_LEFT_RIGHT ||
7336            move_pattern == MV_TURNING_RIGHT_LEFT ||
7337            move_pattern == MV_TURNING_RANDOM ||
7338            move_pattern == MV_ALL_DIRECTIONS)
7339   {
7340     boolean can_turn_left =
7341       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7342     boolean can_turn_right =
7343       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7344
7345     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7346       return;
7347
7348     if (move_pattern == MV_TURNING_LEFT)
7349       MovDir[x][y] = left_dir;
7350     else if (move_pattern == MV_TURNING_RIGHT)
7351       MovDir[x][y] = right_dir;
7352     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7353       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7354     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7355       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7356     else if (move_pattern == MV_TURNING_RANDOM)
7357       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7358                       can_turn_right && !can_turn_left ? right_dir :
7359                       RND(2) ? left_dir : right_dir);
7360     else if (can_turn_left && can_turn_right)
7361       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7362     else if (can_turn_left)
7363       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7364     else if (can_turn_right)
7365       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7366     else
7367       MovDir[x][y] = back_dir;
7368
7369     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7370   }
7371   else if (move_pattern == MV_HORIZONTAL ||
7372            move_pattern == MV_VERTICAL)
7373   {
7374     if (move_pattern & old_move_dir)
7375       MovDir[x][y] = back_dir;
7376     else if (move_pattern == MV_HORIZONTAL)
7377       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7378     else if (move_pattern == MV_VERTICAL)
7379       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7380
7381     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7382   }
7383   else if (move_pattern & MV_ANY_DIRECTION)
7384   {
7385     MovDir[x][y] = move_pattern;
7386     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7387   }
7388   else if (move_pattern & MV_WIND_DIRECTION)
7389   {
7390     MovDir[x][y] = game.wind_direction;
7391     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7392   }
7393   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7394   {
7395     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7396       MovDir[x][y] = left_dir;
7397     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7398       MovDir[x][y] = right_dir;
7399
7400     if (MovDir[x][y] != old_move_dir)
7401       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7402   }
7403   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7404   {
7405     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7406       MovDir[x][y] = right_dir;
7407     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7408       MovDir[x][y] = left_dir;
7409
7410     if (MovDir[x][y] != old_move_dir)
7411       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7412   }
7413   else if (move_pattern == MV_TOWARDS_PLAYER ||
7414            move_pattern == MV_AWAY_FROM_PLAYER)
7415   {
7416     int attr_x = -1, attr_y = -1;
7417     int newx, newy;
7418     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7419
7420     if (AllPlayersGone)
7421     {
7422       attr_x = ExitX;
7423       attr_y = ExitY;
7424     }
7425     else
7426     {
7427       int i;
7428
7429       for (i = 0; i < MAX_PLAYERS; i++)
7430       {
7431         struct PlayerInfo *player = &stored_player[i];
7432         int jx = player->jx, jy = player->jy;
7433
7434         if (!player->active)
7435           continue;
7436
7437         if (attr_x == -1 ||
7438             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7439         {
7440           attr_x = jx;
7441           attr_y = jy;
7442         }
7443       }
7444     }
7445
7446     MovDir[x][y] = MV_NONE;
7447     if (attr_x < x)
7448       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7449     else if (attr_x > x)
7450       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7451     if (attr_y < y)
7452       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7453     else if (attr_y > y)
7454       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7455
7456     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7457
7458     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7459     {
7460       boolean first_horiz = RND(2);
7461       int new_move_dir = MovDir[x][y];
7462
7463       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7464       {
7465         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7466         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7467
7468         return;
7469       }
7470
7471       MovDir[x][y] =
7472         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7473       Moving2Blocked(x, y, &newx, &newy);
7474
7475       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7476         return;
7477
7478       MovDir[x][y] =
7479         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7480       Moving2Blocked(x, y, &newx, &newy);
7481
7482       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7483         return;
7484
7485       MovDir[x][y] = old_move_dir;
7486     }
7487   }
7488   else if (move_pattern == MV_WHEN_PUSHED ||
7489            move_pattern == MV_WHEN_DROPPED)
7490   {
7491     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7492       MovDir[x][y] = MV_NONE;
7493
7494     MovDelay[x][y] = 0;
7495   }
7496   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7497   {
7498     static int test_xy[7][2] =
7499     {
7500       { 0, -1 },
7501       { -1, 0 },
7502       { +1, 0 },
7503       { 0, +1 },
7504       { 0, -1 },
7505       { -1, 0 },
7506       { +1, 0 },
7507     };
7508     static int test_dir[7] =
7509     {
7510       MV_UP,
7511       MV_LEFT,
7512       MV_RIGHT,
7513       MV_DOWN,
7514       MV_UP,
7515       MV_LEFT,
7516       MV_RIGHT,
7517     };
7518     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7519     int move_preference = -1000000;     /* start with very low preference */
7520     int new_move_dir = MV_NONE;
7521     int start_test = RND(4);
7522     int i;
7523
7524     for (i = 0; i < NUM_DIRECTIONS; i++)
7525     {
7526       int move_dir = test_dir[start_test + i];
7527       int move_dir_preference;
7528
7529       xx = x + test_xy[start_test + i][0];
7530       yy = y + test_xy[start_test + i][1];
7531
7532       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7533           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7534       {
7535         new_move_dir = move_dir;
7536
7537         break;
7538       }
7539
7540       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7541         continue;
7542
7543       move_dir_preference = -1 * RunnerVisit[xx][yy];
7544       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7545         move_dir_preference = PlayerVisit[xx][yy];
7546
7547       if (move_dir_preference > move_preference)
7548       {
7549         /* prefer field that has not been visited for the longest time */
7550         move_preference = move_dir_preference;
7551         new_move_dir = move_dir;
7552       }
7553       else if (move_dir_preference == move_preference &&
7554                move_dir == old_move_dir)
7555       {
7556         /* prefer last direction when all directions are preferred equally */
7557         move_preference = move_dir_preference;
7558         new_move_dir = move_dir;
7559       }
7560     }
7561
7562     MovDir[x][y] = new_move_dir;
7563     if (old_move_dir != new_move_dir)
7564       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7565   }
7566 }
7567
7568 static void TurnRound(int x, int y)
7569 {
7570   int direction = MovDir[x][y];
7571
7572   TurnRoundExt(x, y);
7573
7574   GfxDir[x][y] = MovDir[x][y];
7575
7576   if (direction != MovDir[x][y])
7577     GfxFrame[x][y] = 0;
7578
7579   if (MovDelay[x][y])
7580     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7581
7582   ResetGfxFrame(x, y, FALSE);
7583 }
7584
7585 static boolean JustBeingPushed(int x, int y)
7586 {
7587   int i;
7588
7589   for (i = 0; i < MAX_PLAYERS; i++)
7590   {
7591     struct PlayerInfo *player = &stored_player[i];
7592
7593     if (player->active && player->is_pushing && player->MovPos)
7594     {
7595       int next_jx = player->jx + (player->jx - player->last_jx);
7596       int next_jy = player->jy + (player->jy - player->last_jy);
7597
7598       if (x == next_jx && y == next_jy)
7599         return TRUE;
7600     }
7601   }
7602
7603   return FALSE;
7604 }
7605
7606 void StartMoving(int x, int y)
7607 {
7608   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7609   int element = Feld[x][y];
7610
7611   if (Stop[x][y])
7612     return;
7613
7614   if (MovDelay[x][y] == 0)
7615     GfxAction[x][y] = ACTION_DEFAULT;
7616
7617   if (CAN_FALL(element) && y < lev_fieldy - 1)
7618   {
7619     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7620         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7621       if (JustBeingPushed(x, y))
7622         return;
7623
7624     if (element == EL_QUICKSAND_FULL)
7625     {
7626       if (IS_FREE(x, y + 1))
7627       {
7628         InitMovingField(x, y, MV_DOWN);
7629         started_moving = TRUE;
7630
7631         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7632 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7633         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7634           Store[x][y] = EL_ROCK;
7635 #else
7636         Store[x][y] = EL_ROCK;
7637 #endif
7638
7639         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7640       }
7641       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7642       {
7643         if (!MovDelay[x][y])
7644         {
7645           MovDelay[x][y] = TILEY + 1;
7646
7647           ResetGfxAnimation(x, y);
7648           ResetGfxAnimation(x, y + 1);
7649         }
7650
7651         if (MovDelay[x][y])
7652         {
7653           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7654           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7655
7656           MovDelay[x][y]--;
7657           if (MovDelay[x][y])
7658             return;
7659         }
7660
7661         Feld[x][y] = EL_QUICKSAND_EMPTY;
7662         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7663         Store[x][y + 1] = Store[x][y];
7664         Store[x][y] = 0;
7665
7666         PlayLevelSoundAction(x, y, ACTION_FILLING);
7667       }
7668       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7669       {
7670         if (!MovDelay[x][y])
7671         {
7672           MovDelay[x][y] = TILEY + 1;
7673
7674           ResetGfxAnimation(x, y);
7675           ResetGfxAnimation(x, y + 1);
7676         }
7677
7678         if (MovDelay[x][y])
7679         {
7680           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7681           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7682
7683           MovDelay[x][y]--;
7684           if (MovDelay[x][y])
7685             return;
7686         }
7687
7688         Feld[x][y] = EL_QUICKSAND_EMPTY;
7689         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7690         Store[x][y + 1] = Store[x][y];
7691         Store[x][y] = 0;
7692
7693         PlayLevelSoundAction(x, y, ACTION_FILLING);
7694       }
7695     }
7696     else if (element == EL_QUICKSAND_FAST_FULL)
7697     {
7698       if (IS_FREE(x, y + 1))
7699       {
7700         InitMovingField(x, y, MV_DOWN);
7701         started_moving = TRUE;
7702
7703         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7704 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7705         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7706           Store[x][y] = EL_ROCK;
7707 #else
7708         Store[x][y] = EL_ROCK;
7709 #endif
7710
7711         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7712       }
7713       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7714       {
7715         if (!MovDelay[x][y])
7716         {
7717           MovDelay[x][y] = TILEY + 1;
7718
7719           ResetGfxAnimation(x, y);
7720           ResetGfxAnimation(x, y + 1);
7721         }
7722
7723         if (MovDelay[x][y])
7724         {
7725           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7726           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7727
7728           MovDelay[x][y]--;
7729           if (MovDelay[x][y])
7730             return;
7731         }
7732
7733         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7734         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7735         Store[x][y + 1] = Store[x][y];
7736         Store[x][y] = 0;
7737
7738         PlayLevelSoundAction(x, y, ACTION_FILLING);
7739       }
7740       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7741       {
7742         if (!MovDelay[x][y])
7743         {
7744           MovDelay[x][y] = TILEY + 1;
7745
7746           ResetGfxAnimation(x, y);
7747           ResetGfxAnimation(x, y + 1);
7748         }
7749
7750         if (MovDelay[x][y])
7751         {
7752           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7753           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7754
7755           MovDelay[x][y]--;
7756           if (MovDelay[x][y])
7757             return;
7758         }
7759
7760         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7761         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7762         Store[x][y + 1] = Store[x][y];
7763         Store[x][y] = 0;
7764
7765         PlayLevelSoundAction(x, y, ACTION_FILLING);
7766       }
7767     }
7768     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7769              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7770     {
7771       InitMovingField(x, y, MV_DOWN);
7772       started_moving = TRUE;
7773
7774       Feld[x][y] = EL_QUICKSAND_FILLING;
7775       Store[x][y] = element;
7776
7777       PlayLevelSoundAction(x, y, ACTION_FILLING);
7778     }
7779     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7780              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7781     {
7782       InitMovingField(x, y, MV_DOWN);
7783       started_moving = TRUE;
7784
7785       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7786       Store[x][y] = element;
7787
7788       PlayLevelSoundAction(x, y, ACTION_FILLING);
7789     }
7790     else if (element == EL_MAGIC_WALL_FULL)
7791     {
7792       if (IS_FREE(x, y + 1))
7793       {
7794         InitMovingField(x, y, MV_DOWN);
7795         started_moving = TRUE;
7796
7797         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7798         Store[x][y] = EL_CHANGED(Store[x][y]);
7799       }
7800       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7801       {
7802         if (!MovDelay[x][y])
7803           MovDelay[x][y] = TILEY/4 + 1;
7804
7805         if (MovDelay[x][y])
7806         {
7807           MovDelay[x][y]--;
7808           if (MovDelay[x][y])
7809             return;
7810         }
7811
7812         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7813         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7814         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7815         Store[x][y] = 0;
7816       }
7817     }
7818     else if (element == EL_BD_MAGIC_WALL_FULL)
7819     {
7820       if (IS_FREE(x, y + 1))
7821       {
7822         InitMovingField(x, y, MV_DOWN);
7823         started_moving = TRUE;
7824
7825         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7826         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7827       }
7828       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7829       {
7830         if (!MovDelay[x][y])
7831           MovDelay[x][y] = TILEY/4 + 1;
7832
7833         if (MovDelay[x][y])
7834         {
7835           MovDelay[x][y]--;
7836           if (MovDelay[x][y])
7837             return;
7838         }
7839
7840         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7841         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7842         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7843         Store[x][y] = 0;
7844       }
7845     }
7846     else if (element == EL_DC_MAGIC_WALL_FULL)
7847     {
7848       if (IS_FREE(x, y + 1))
7849       {
7850         InitMovingField(x, y, MV_DOWN);
7851         started_moving = TRUE;
7852
7853         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7854         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7855       }
7856       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7857       {
7858         if (!MovDelay[x][y])
7859           MovDelay[x][y] = TILEY/4 + 1;
7860
7861         if (MovDelay[x][y])
7862         {
7863           MovDelay[x][y]--;
7864           if (MovDelay[x][y])
7865             return;
7866         }
7867
7868         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7869         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7870         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7871         Store[x][y] = 0;
7872       }
7873     }
7874     else if ((CAN_PASS_MAGIC_WALL(element) &&
7875               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7876                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7877              (CAN_PASS_DC_MAGIC_WALL(element) &&
7878               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7879
7880     {
7881       InitMovingField(x, y, MV_DOWN);
7882       started_moving = TRUE;
7883
7884       Feld[x][y] =
7885         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7886          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7887          EL_DC_MAGIC_WALL_FILLING);
7888       Store[x][y] = element;
7889     }
7890     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7891     {
7892       SplashAcid(x, y + 1);
7893
7894       InitMovingField(x, y, MV_DOWN);
7895       started_moving = TRUE;
7896
7897       Store[x][y] = EL_ACID;
7898     }
7899     else if (
7900 #if USE_FIX_IMPACT_COLLISION
7901              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7902               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7903 #else
7904              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7905               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7906 #endif
7907              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7908               CAN_FALL(element) && WasJustFalling[x][y] &&
7909               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7910
7911              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7912               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7913               (Feld[x][y + 1] == EL_BLOCKED)))
7914     {
7915       /* this is needed for a special case not covered by calling "Impact()"
7916          from "ContinueMoving()": if an element moves to a tile directly below
7917          another element which was just falling on that tile (which was empty
7918          in the previous frame), the falling element above would just stop
7919          instead of smashing the element below (in previous version, the above
7920          element was just checked for "moving" instead of "falling", resulting
7921          in incorrect smashes caused by horizontal movement of the above
7922          element; also, the case of the player being the element to smash was
7923          simply not covered here... :-/ ) */
7924
7925       CheckCollision[x][y] = 0;
7926       CheckImpact[x][y] = 0;
7927
7928       Impact(x, y);
7929     }
7930     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7931     {
7932       if (MovDir[x][y] == MV_NONE)
7933       {
7934         InitMovingField(x, y, MV_DOWN);
7935         started_moving = TRUE;
7936       }
7937     }
7938     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7939     {
7940       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7941         MovDir[x][y] = MV_DOWN;
7942
7943       InitMovingField(x, y, MV_DOWN);
7944       started_moving = TRUE;
7945     }
7946     else if (element == EL_AMOEBA_DROP)
7947     {
7948       Feld[x][y] = EL_AMOEBA_GROWING;
7949       Store[x][y] = EL_AMOEBA_WET;
7950     }
7951     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7952               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7953              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7954              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7955     {
7956       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7957                                 (IS_FREE(x - 1, y + 1) ||
7958                                  Feld[x - 1][y + 1] == EL_ACID));
7959       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7960                                 (IS_FREE(x + 1, y + 1) ||
7961                                  Feld[x + 1][y + 1] == EL_ACID));
7962       boolean can_fall_any  = (can_fall_left || can_fall_right);
7963       boolean can_fall_both = (can_fall_left && can_fall_right);
7964       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7965
7966 #if USE_NEW_ALL_SLIPPERY
7967       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7968       {
7969         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7970           can_fall_right = FALSE;
7971         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7972           can_fall_left = FALSE;
7973         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7974           can_fall_right = FALSE;
7975         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7976           can_fall_left = FALSE;
7977
7978         can_fall_any  = (can_fall_left || can_fall_right);
7979         can_fall_both = FALSE;
7980       }
7981 #else
7982       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7983       {
7984         if (slippery_type == SLIPPERY_ONLY_LEFT)
7985           can_fall_right = FALSE;
7986         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7987           can_fall_left = FALSE;
7988         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7989           can_fall_right = FALSE;
7990         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7991           can_fall_left = FALSE;
7992
7993         can_fall_any  = (can_fall_left || can_fall_right);
7994         can_fall_both = (can_fall_left && can_fall_right);
7995       }
7996 #endif
7997
7998 #if USE_NEW_ALL_SLIPPERY
7999 #else
8000 #if USE_NEW_SP_SLIPPERY
8001       /* !!! better use the same properties as for custom elements here !!! */
8002       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8003                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8004       {
8005         can_fall_right = FALSE;         /* slip down on left side */
8006         can_fall_both = FALSE;
8007       }
8008 #endif
8009 #endif
8010
8011 #if USE_NEW_ALL_SLIPPERY
8012       if (can_fall_both)
8013       {
8014         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8015           can_fall_right = FALSE;       /* slip down on left side */
8016         else
8017           can_fall_left = !(can_fall_right = RND(2));
8018
8019         can_fall_both = FALSE;
8020       }
8021 #else
8022       if (can_fall_both)
8023       {
8024         if (game.emulation == EMU_BOULDERDASH ||
8025             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8026           can_fall_right = FALSE;       /* slip down on left side */
8027         else
8028           can_fall_left = !(can_fall_right = RND(2));
8029
8030         can_fall_both = FALSE;
8031       }
8032 #endif
8033
8034       if (can_fall_any)
8035       {
8036         /* if not determined otherwise, prefer left side for slipping down */
8037         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8038         started_moving = TRUE;
8039       }
8040     }
8041 #if 0
8042     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8043 #else
8044     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8045 #endif
8046     {
8047       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8048       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8049       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8050       int belt_dir = game.belt_dir[belt_nr];
8051
8052       if ((belt_dir == MV_LEFT  && left_is_free) ||
8053           (belt_dir == MV_RIGHT && right_is_free))
8054       {
8055         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8056
8057         InitMovingField(x, y, belt_dir);
8058         started_moving = TRUE;
8059
8060         Pushed[x][y] = TRUE;
8061         Pushed[nextx][y] = TRUE;
8062
8063         GfxAction[x][y] = ACTION_DEFAULT;
8064       }
8065       else
8066       {
8067         MovDir[x][y] = 0;       /* if element was moving, stop it */
8068       }
8069     }
8070   }
8071
8072   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8073 #if 0
8074   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8075 #else
8076   if (CAN_MOVE(element) && !started_moving)
8077 #endif
8078   {
8079     int move_pattern = element_info[element].move_pattern;
8080     int newx, newy;
8081
8082 #if 0
8083 #if DEBUG
8084     if (MovDir[x][y] == MV_NONE)
8085     {
8086       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8087              x, y, element, element_info[element].token_name);
8088       printf("StartMoving(): This should never happen!\n");
8089     }
8090 #endif
8091 #endif
8092
8093     Moving2Blocked(x, y, &newx, &newy);
8094
8095     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8096       return;
8097
8098     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8099         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8100     {
8101       WasJustMoving[x][y] = 0;
8102       CheckCollision[x][y] = 0;
8103
8104       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8105
8106       if (Feld[x][y] != element)        /* element has changed */
8107         return;
8108     }
8109
8110     if (!MovDelay[x][y])        /* start new movement phase */
8111     {
8112       /* all objects that can change their move direction after each step
8113          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8114
8115       if (element != EL_YAMYAM &&
8116           element != EL_DARK_YAMYAM &&
8117           element != EL_PACMAN &&
8118           !(move_pattern & MV_ANY_DIRECTION) &&
8119           move_pattern != MV_TURNING_LEFT &&
8120           move_pattern != MV_TURNING_RIGHT &&
8121           move_pattern != MV_TURNING_LEFT_RIGHT &&
8122           move_pattern != MV_TURNING_RIGHT_LEFT &&
8123           move_pattern != MV_TURNING_RANDOM)
8124       {
8125         TurnRound(x, y);
8126
8127         if (MovDelay[x][y] && (element == EL_BUG ||
8128                                element == EL_SPACESHIP ||
8129                                element == EL_SP_SNIKSNAK ||
8130                                element == EL_SP_ELECTRON ||
8131                                element == EL_MOLE))
8132           TEST_DrawLevelField(x, y);
8133       }
8134     }
8135
8136     if (MovDelay[x][y])         /* wait some time before next movement */
8137     {
8138       MovDelay[x][y]--;
8139
8140       if (element == EL_ROBOT ||
8141           element == EL_YAMYAM ||
8142           element == EL_DARK_YAMYAM)
8143       {
8144         DrawLevelElementAnimationIfNeeded(x, y, element);
8145         PlayLevelSoundAction(x, y, ACTION_WAITING);
8146       }
8147       else if (element == EL_SP_ELECTRON)
8148         DrawLevelElementAnimationIfNeeded(x, y, element);
8149       else if (element == EL_DRAGON)
8150       {
8151         int i;
8152         int dir = MovDir[x][y];
8153         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8154         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8155         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8156                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8157                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8158                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8159         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8160
8161         GfxAction[x][y] = ACTION_ATTACKING;
8162
8163         if (IS_PLAYER(x, y))
8164           DrawPlayerField(x, y);
8165         else
8166           TEST_DrawLevelField(x, y);
8167
8168         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8169
8170         for (i = 1; i <= 3; i++)
8171         {
8172           int xx = x + i * dx;
8173           int yy = y + i * dy;
8174           int sx = SCREENX(xx);
8175           int sy = SCREENY(yy);
8176           int flame_graphic = graphic + (i - 1);
8177
8178           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8179             break;
8180
8181           if (MovDelay[x][y])
8182           {
8183             int flamed = MovingOrBlocked2Element(xx, yy);
8184
8185             /* !!! */
8186 #if 0
8187             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8188               Bang(xx, yy);
8189             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8190               RemoveMovingField(xx, yy);
8191             else
8192               RemoveField(xx, yy);
8193 #else
8194             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8195               Bang(xx, yy);
8196             else
8197               RemoveMovingField(xx, yy);
8198 #endif
8199
8200             ChangeDelay[xx][yy] = 0;
8201
8202             Feld[xx][yy] = EL_FLAMES;
8203
8204             if (IN_SCR_FIELD(sx, sy))
8205             {
8206               TEST_DrawLevelFieldCrumbledSand(xx, yy);
8207               DrawGraphic(sx, sy, flame_graphic, frame);
8208             }
8209           }
8210           else
8211           {
8212             if (Feld[xx][yy] == EL_FLAMES)
8213               Feld[xx][yy] = EL_EMPTY;
8214             TEST_DrawLevelField(xx, yy);
8215           }
8216         }
8217       }
8218
8219       if (MovDelay[x][y])       /* element still has to wait some time */
8220       {
8221         PlayLevelSoundAction(x, y, ACTION_WAITING);
8222
8223         return;
8224       }
8225     }
8226
8227     /* now make next step */
8228
8229     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8230
8231     if (DONT_COLLIDE_WITH(element) &&
8232         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8233         !PLAYER_ENEMY_PROTECTED(newx, newy))
8234     {
8235       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8236
8237       return;
8238     }
8239
8240     else if (CAN_MOVE_INTO_ACID(element) &&
8241              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8242              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8243              (MovDir[x][y] == MV_DOWN ||
8244               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8245     {
8246       SplashAcid(newx, newy);
8247       Store[x][y] = EL_ACID;
8248     }
8249     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8250     {
8251       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8252           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8253           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8254           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8255       {
8256         RemoveField(x, y);
8257         TEST_DrawLevelField(x, y);
8258
8259         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8260         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8261           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8262
8263         local_player->friends_still_needed--;
8264         if (!local_player->friends_still_needed &&
8265             !local_player->GameOver && AllPlayersGone)
8266           PlayerWins(local_player);
8267
8268         return;
8269       }
8270       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8271       {
8272         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8273           TEST_DrawLevelField(newx, newy);
8274         else
8275           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8276       }
8277       else if (!IS_FREE(newx, newy))
8278       {
8279         GfxAction[x][y] = ACTION_WAITING;
8280
8281         if (IS_PLAYER(x, y))
8282           DrawPlayerField(x, y);
8283         else
8284           TEST_DrawLevelField(x, y);
8285
8286         return;
8287       }
8288     }
8289     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8290     {
8291       if (IS_FOOD_PIG(Feld[newx][newy]))
8292       {
8293         if (IS_MOVING(newx, newy))
8294           RemoveMovingField(newx, newy);
8295         else
8296         {
8297           Feld[newx][newy] = EL_EMPTY;
8298           TEST_DrawLevelField(newx, newy);
8299         }
8300
8301         PlayLevelSound(x, y, SND_PIG_DIGGING);
8302       }
8303       else if (!IS_FREE(newx, newy))
8304       {
8305         if (IS_PLAYER(x, y))
8306           DrawPlayerField(x, y);
8307         else
8308           TEST_DrawLevelField(x, y);
8309
8310         return;
8311       }
8312     }
8313     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8314     {
8315       if (Store[x][y] != EL_EMPTY)
8316       {
8317         boolean can_clone = FALSE;
8318         int xx, yy;
8319
8320         /* check if element to clone is still there */
8321         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8322         {
8323           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8324           {
8325             can_clone = TRUE;
8326
8327             break;
8328           }
8329         }
8330
8331         /* cannot clone or target field not free anymore -- do not clone */
8332         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8333           Store[x][y] = EL_EMPTY;
8334       }
8335
8336       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8337       {
8338         if (IS_MV_DIAGONAL(MovDir[x][y]))
8339         {
8340           int diagonal_move_dir = MovDir[x][y];
8341           int stored = Store[x][y];
8342           int change_delay = 8;
8343           int graphic;
8344
8345           /* android is moving diagonally */
8346
8347           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8348
8349           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8350           GfxElement[x][y] = EL_EMC_ANDROID;
8351           GfxAction[x][y] = ACTION_SHRINKING;
8352           GfxDir[x][y] = diagonal_move_dir;
8353           ChangeDelay[x][y] = change_delay;
8354
8355           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8356                                    GfxDir[x][y]);
8357
8358           DrawLevelGraphicAnimation(x, y, graphic);
8359           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8360
8361           if (Feld[newx][newy] == EL_ACID)
8362           {
8363             SplashAcid(newx, newy);
8364
8365             return;
8366           }
8367
8368           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8369
8370           Store[newx][newy] = EL_EMC_ANDROID;
8371           GfxElement[newx][newy] = EL_EMC_ANDROID;
8372           GfxAction[newx][newy] = ACTION_GROWING;
8373           GfxDir[newx][newy] = diagonal_move_dir;
8374           ChangeDelay[newx][newy] = change_delay;
8375
8376           graphic = el_act_dir2img(GfxElement[newx][newy],
8377                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8378
8379           DrawLevelGraphicAnimation(newx, newy, graphic);
8380           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8381
8382           return;
8383         }
8384         else
8385         {
8386           Feld[newx][newy] = EL_EMPTY;
8387           TEST_DrawLevelField(newx, newy);
8388
8389           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8390         }
8391       }
8392       else if (!IS_FREE(newx, newy))
8393       {
8394 #if 0
8395         if (IS_PLAYER(x, y))
8396           DrawPlayerField(x, y);
8397         else
8398           TEST_DrawLevelField(x, y);
8399 #endif
8400
8401         return;
8402       }
8403     }
8404     else if (IS_CUSTOM_ELEMENT(element) &&
8405              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8406     {
8407 #if 1
8408       if (!DigFieldByCE(newx, newy, element))
8409         return;
8410 #else
8411       int new_element = Feld[newx][newy];
8412
8413       if (!IS_FREE(newx, newy))
8414       {
8415         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8416                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8417                       ACTION_BREAKING);
8418
8419         /* no element can dig solid indestructible elements */
8420         if (IS_INDESTRUCTIBLE(new_element) &&
8421             !IS_DIGGABLE(new_element) &&
8422             !IS_COLLECTIBLE(new_element))
8423           return;
8424
8425         if (AmoebaNr[newx][newy] &&
8426             (new_element == EL_AMOEBA_FULL ||
8427              new_element == EL_BD_AMOEBA ||
8428              new_element == EL_AMOEBA_GROWING))
8429         {
8430           AmoebaCnt[AmoebaNr[newx][newy]]--;
8431           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8432         }
8433
8434         if (IS_MOVING(newx, newy))
8435           RemoveMovingField(newx, newy);
8436         else
8437         {
8438           RemoveField(newx, newy);
8439           TEST_DrawLevelField(newx, newy);
8440         }
8441
8442         /* if digged element was about to explode, prevent the explosion */
8443         ExplodeField[newx][newy] = EX_TYPE_NONE;
8444
8445         PlayLevelSoundAction(x, y, action);
8446       }
8447
8448       Store[newx][newy] = EL_EMPTY;
8449
8450 #if 1
8451       /* this makes it possible to leave the removed element again */
8452       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8453         Store[newx][newy] = new_element;
8454 #else
8455       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8456       {
8457         int move_leave_element = element_info[element].move_leave_element;
8458
8459         /* this makes it possible to leave the removed element again */
8460         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8461                              new_element : move_leave_element);
8462       }
8463 #endif
8464
8465 #endif
8466
8467       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8468       {
8469         RunnerVisit[x][y] = FrameCounter;
8470         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8471       }
8472     }
8473     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8474     {
8475       if (!IS_FREE(newx, newy))
8476       {
8477         if (IS_PLAYER(x, y))
8478           DrawPlayerField(x, y);
8479         else
8480           TEST_DrawLevelField(x, y);
8481
8482         return;
8483       }
8484       else
8485       {
8486         boolean wanna_flame = !RND(10);
8487         int dx = newx - x, dy = newy - y;
8488         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8489         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8490         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8491                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8492         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8493                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8494
8495         if ((wanna_flame ||
8496              IS_CLASSIC_ENEMY(element1) ||
8497              IS_CLASSIC_ENEMY(element2)) &&
8498             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8499             element1 != EL_FLAMES && element2 != EL_FLAMES)
8500         {
8501           ResetGfxAnimation(x, y);
8502           GfxAction[x][y] = ACTION_ATTACKING;
8503
8504           if (IS_PLAYER(x, y))
8505             DrawPlayerField(x, y);
8506           else
8507             TEST_DrawLevelField(x, y);
8508
8509           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8510
8511           MovDelay[x][y] = 50;
8512
8513           /* !!! */
8514 #if 0
8515           RemoveField(newx, newy);
8516 #endif
8517           Feld[newx][newy] = EL_FLAMES;
8518           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8519           {
8520 #if 0
8521             RemoveField(newx1, newy1);
8522 #endif
8523             Feld[newx1][newy1] = EL_FLAMES;
8524           }
8525           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8526           {
8527 #if 0
8528             RemoveField(newx2, newy2);
8529 #endif
8530             Feld[newx2][newy2] = EL_FLAMES;
8531           }
8532
8533           return;
8534         }
8535       }
8536     }
8537     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8538              Feld[newx][newy] == EL_DIAMOND)
8539     {
8540       if (IS_MOVING(newx, newy))
8541         RemoveMovingField(newx, newy);
8542       else
8543       {
8544         Feld[newx][newy] = EL_EMPTY;
8545         TEST_DrawLevelField(newx, newy);
8546       }
8547
8548       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8549     }
8550     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8551              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8552     {
8553       if (AmoebaNr[newx][newy])
8554       {
8555         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8556         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8557             Feld[newx][newy] == EL_BD_AMOEBA)
8558           AmoebaCnt[AmoebaNr[newx][newy]]--;
8559       }
8560
8561 #if 0
8562       /* !!! test !!! */
8563       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8564       {
8565         RemoveMovingField(newx, newy);
8566       }
8567 #else
8568       if (IS_MOVING(newx, newy))
8569       {
8570         RemoveMovingField(newx, newy);
8571       }
8572 #endif
8573       else
8574       {
8575         Feld[newx][newy] = EL_EMPTY;
8576         TEST_DrawLevelField(newx, newy);
8577       }
8578
8579       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8580     }
8581     else if ((element == EL_PACMAN || element == EL_MOLE)
8582              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8583     {
8584       if (AmoebaNr[newx][newy])
8585       {
8586         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8587         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8588             Feld[newx][newy] == EL_BD_AMOEBA)
8589           AmoebaCnt[AmoebaNr[newx][newy]]--;
8590       }
8591
8592       if (element == EL_MOLE)
8593       {
8594         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8595         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8596
8597         ResetGfxAnimation(x, y);
8598         GfxAction[x][y] = ACTION_DIGGING;
8599         TEST_DrawLevelField(x, y);
8600
8601         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8602
8603         return;                         /* wait for shrinking amoeba */
8604       }
8605       else      /* element == EL_PACMAN */
8606       {
8607         Feld[newx][newy] = EL_EMPTY;
8608         TEST_DrawLevelField(newx, newy);
8609         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8610       }
8611     }
8612     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8613              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8614               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8615     {
8616       /* wait for shrinking amoeba to completely disappear */
8617       return;
8618     }
8619     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8620     {
8621       /* object was running against a wall */
8622
8623       TurnRound(x, y);
8624
8625 #if 0
8626       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8627       if (move_pattern & MV_ANY_DIRECTION &&
8628           move_pattern == MovDir[x][y])
8629       {
8630         int blocking_element =
8631           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8632
8633         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8634                                  MovDir[x][y]);
8635
8636         element = Feld[x][y];   /* element might have changed */
8637       }
8638 #endif
8639
8640       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8641         DrawLevelElementAnimation(x, y, element);
8642
8643       if (DONT_TOUCH(element))
8644         TestIfBadThingTouchesPlayer(x, y);
8645
8646       return;
8647     }
8648
8649     InitMovingField(x, y, MovDir[x][y]);
8650
8651     PlayLevelSoundAction(x, y, ACTION_MOVING);
8652   }
8653
8654   if (MovDir[x][y])
8655     ContinueMoving(x, y);
8656 }
8657
8658 void ContinueMoving(int x, int y)
8659 {
8660   int element = Feld[x][y];
8661   struct ElementInfo *ei = &element_info[element];
8662   int direction = MovDir[x][y];
8663   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8664   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8665   int newx = x + dx, newy = y + dy;
8666   int stored = Store[x][y];
8667   int stored_new = Store[newx][newy];
8668   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8669   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8670   boolean last_line = (newy == lev_fieldy - 1);
8671
8672   MovPos[x][y] += getElementMoveStepsize(x, y);
8673
8674   if (pushed_by_player) /* special case: moving object pushed by player */
8675     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8676
8677   if (ABS(MovPos[x][y]) < TILEX)
8678   {
8679 #if 0
8680     int ee = Feld[x][y];
8681     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8682     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8683
8684     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8685            x, y, ABS(MovPos[x][y]),
8686            ee, gg, ff,
8687            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8688 #endif
8689
8690     TEST_DrawLevelField(x, y);
8691
8692     return;     /* element is still moving */
8693   }
8694
8695   /* element reached destination field */
8696
8697   Feld[x][y] = EL_EMPTY;
8698   Feld[newx][newy] = element;
8699   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8700
8701   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8702   {
8703     element = Feld[newx][newy] = EL_ACID;
8704   }
8705   else if (element == EL_MOLE)
8706   {
8707     Feld[x][y] = EL_SAND;
8708
8709     TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8710   }
8711   else if (element == EL_QUICKSAND_FILLING)
8712   {
8713     element = Feld[newx][newy] = get_next_element(element);
8714     Store[newx][newy] = Store[x][y];
8715   }
8716   else if (element == EL_QUICKSAND_EMPTYING)
8717   {
8718     Feld[x][y] = get_next_element(element);
8719     element = Feld[newx][newy] = Store[x][y];
8720   }
8721   else if (element == EL_QUICKSAND_FAST_FILLING)
8722   {
8723     element = Feld[newx][newy] = get_next_element(element);
8724     Store[newx][newy] = Store[x][y];
8725   }
8726   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8727   {
8728     Feld[x][y] = get_next_element(element);
8729     element = Feld[newx][newy] = Store[x][y];
8730   }
8731   else if (element == EL_MAGIC_WALL_FILLING)
8732   {
8733     element = Feld[newx][newy] = get_next_element(element);
8734     if (!game.magic_wall_active)
8735       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8736     Store[newx][newy] = Store[x][y];
8737   }
8738   else if (element == EL_MAGIC_WALL_EMPTYING)
8739   {
8740     Feld[x][y] = get_next_element(element);
8741     if (!game.magic_wall_active)
8742       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8743     element = Feld[newx][newy] = Store[x][y];
8744
8745 #if USE_NEW_CUSTOM_VALUE
8746     InitField(newx, newy, FALSE);
8747 #endif
8748   }
8749   else if (element == EL_BD_MAGIC_WALL_FILLING)
8750   {
8751     element = Feld[newx][newy] = get_next_element(element);
8752     if (!game.magic_wall_active)
8753       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8754     Store[newx][newy] = Store[x][y];
8755   }
8756   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8757   {
8758     Feld[x][y] = get_next_element(element);
8759     if (!game.magic_wall_active)
8760       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8761     element = Feld[newx][newy] = Store[x][y];
8762
8763 #if USE_NEW_CUSTOM_VALUE
8764     InitField(newx, newy, FALSE);
8765 #endif
8766   }
8767   else if (element == EL_DC_MAGIC_WALL_FILLING)
8768   {
8769     element = Feld[newx][newy] = get_next_element(element);
8770     if (!game.magic_wall_active)
8771       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8772     Store[newx][newy] = Store[x][y];
8773   }
8774   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8775   {
8776     Feld[x][y] = get_next_element(element);
8777     if (!game.magic_wall_active)
8778       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8779     element = Feld[newx][newy] = Store[x][y];
8780
8781 #if USE_NEW_CUSTOM_VALUE
8782     InitField(newx, newy, FALSE);
8783 #endif
8784   }
8785   else if (element == EL_AMOEBA_DROPPING)
8786   {
8787     Feld[x][y] = get_next_element(element);
8788     element = Feld[newx][newy] = Store[x][y];
8789   }
8790   else if (element == EL_SOKOBAN_OBJECT)
8791   {
8792     if (Back[x][y])
8793       Feld[x][y] = Back[x][y];
8794
8795     if (Back[newx][newy])
8796       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8797
8798     Back[x][y] = Back[newx][newy] = 0;
8799   }
8800
8801   Store[x][y] = EL_EMPTY;
8802   MovPos[x][y] = 0;
8803   MovDir[x][y] = 0;
8804   MovDelay[x][y] = 0;
8805
8806   MovDelay[newx][newy] = 0;
8807
8808   if (CAN_CHANGE_OR_HAS_ACTION(element))
8809   {
8810     /* copy element change control values to new field */
8811     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8812     ChangePage[newx][newy]  = ChangePage[x][y];
8813     ChangeCount[newx][newy] = ChangeCount[x][y];
8814     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8815   }
8816
8817 #if USE_NEW_CUSTOM_VALUE
8818   CustomValue[newx][newy] = CustomValue[x][y];
8819 #endif
8820
8821   ChangeDelay[x][y] = 0;
8822   ChangePage[x][y] = -1;
8823   ChangeCount[x][y] = 0;
8824   ChangeEvent[x][y] = -1;
8825
8826 #if USE_NEW_CUSTOM_VALUE
8827   CustomValue[x][y] = 0;
8828 #endif
8829
8830   /* copy animation control values to new field */
8831   GfxFrame[newx][newy]  = GfxFrame[x][y];
8832   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8833   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8834   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8835
8836   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8837
8838   /* some elements can leave other elements behind after moving */
8839 #if 1
8840   if (ei->move_leave_element != EL_EMPTY &&
8841       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8842       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8843 #else
8844   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8845       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8846       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8847 #endif
8848   {
8849     int move_leave_element = ei->move_leave_element;
8850
8851 #if 1
8852 #if 1
8853     /* this makes it possible to leave the removed element again */
8854     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8855       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8856 #else
8857     /* this makes it possible to leave the removed element again */
8858     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8859       move_leave_element = stored;
8860 #endif
8861 #else
8862     /* this makes it possible to leave the removed element again */
8863     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8864         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8865       move_leave_element = stored;
8866 #endif
8867
8868     Feld[x][y] = move_leave_element;
8869
8870     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8871       MovDir[x][y] = direction;
8872
8873     InitField(x, y, FALSE);
8874
8875     if (GFX_CRUMBLED(Feld[x][y]))
8876       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8877
8878     if (ELEM_IS_PLAYER(move_leave_element))
8879       RelocatePlayer(x, y, move_leave_element);
8880   }
8881
8882   /* do this after checking for left-behind element */
8883   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8884
8885   if (!CAN_MOVE(element) ||
8886       (CAN_FALL(element) && direction == MV_DOWN &&
8887        (element == EL_SPRING ||
8888         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8889         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8890     GfxDir[x][y] = MovDir[newx][newy] = 0;
8891
8892   TEST_DrawLevelField(x, y);
8893   TEST_DrawLevelField(newx, newy);
8894
8895   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8896
8897   /* prevent pushed element from moving on in pushed direction */
8898   if (pushed_by_player && CAN_MOVE(element) &&
8899       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8900       !(element_info[element].move_pattern & direction))
8901     TurnRound(newx, newy);
8902
8903   /* prevent elements on conveyor belt from moving on in last direction */
8904   if (pushed_by_conveyor && CAN_FALL(element) &&
8905       direction & MV_HORIZONTAL)
8906     MovDir[newx][newy] = 0;
8907
8908   if (!pushed_by_player)
8909   {
8910     int nextx = newx + dx, nexty = newy + dy;
8911     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8912
8913     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8914
8915     if (CAN_FALL(element) && direction == MV_DOWN)
8916       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8917
8918     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8919       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8920
8921 #if USE_FIX_IMPACT_COLLISION
8922     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8923       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8924 #endif
8925   }
8926
8927   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8928   {
8929     TestIfBadThingTouchesPlayer(newx, newy);
8930     TestIfBadThingTouchesFriend(newx, newy);
8931
8932     if (!IS_CUSTOM_ELEMENT(element))
8933       TestIfBadThingTouchesOtherBadThing(newx, newy);
8934   }
8935   else if (element == EL_PENGUIN)
8936     TestIfFriendTouchesBadThing(newx, newy);
8937
8938   /* give the player one last chance (one more frame) to move away */
8939   if (CAN_FALL(element) && direction == MV_DOWN &&
8940       (last_line || (!IS_FREE(x, newy + 1) &&
8941                      (!IS_PLAYER(x, newy + 1) ||
8942                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8943     Impact(x, newy);
8944
8945   if (pushed_by_player && !game.use_change_when_pushing_bug)
8946   {
8947     int push_side = MV_DIR_OPPOSITE(direction);
8948     struct PlayerInfo *player = PLAYERINFO(x, y);
8949
8950     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8951                                player->index_bit, push_side);
8952     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8953                                         player->index_bit, push_side);
8954   }
8955
8956   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8957     MovDelay[newx][newy] = 1;
8958
8959   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8960
8961   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8962
8963 #if 0
8964   if (ChangePage[newx][newy] != -1)             /* delayed change */
8965   {
8966     int page = ChangePage[newx][newy];
8967     struct ElementChangeInfo *change = &ei->change_page[page];
8968
8969     ChangePage[newx][newy] = -1;
8970
8971     if (change->can_change)
8972     {
8973       if (ChangeElement(newx, newy, element, page))
8974       {
8975         if (change->post_change_function)
8976           change->post_change_function(newx, newy);
8977       }
8978     }
8979
8980     if (change->has_action)
8981       ExecuteCustomElementAction(newx, newy, element, page);
8982   }
8983 #endif
8984
8985   TestIfElementHitsCustomElement(newx, newy, direction);
8986   TestIfPlayerTouchesCustomElement(newx, newy);
8987   TestIfElementTouchesCustomElement(newx, newy);
8988
8989   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8990       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8991     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8992                              MV_DIR_OPPOSITE(direction));
8993 }
8994
8995 int AmoebeNachbarNr(int ax, int ay)
8996 {
8997   int i;
8998   int element = Feld[ax][ay];
8999   int group_nr = 0;
9000   static int xy[4][2] =
9001   {
9002     { 0, -1 },
9003     { -1, 0 },
9004     { +1, 0 },
9005     { 0, +1 }
9006   };
9007
9008   for (i = 0; i < NUM_DIRECTIONS; i++)
9009   {
9010     int x = ax + xy[i][0];
9011     int y = ay + xy[i][1];
9012
9013     if (!IN_LEV_FIELD(x, y))
9014       continue;
9015
9016     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9017       group_nr = AmoebaNr[x][y];
9018   }
9019
9020   return group_nr;
9021 }
9022
9023 void AmoebenVereinigen(int ax, int ay)
9024 {
9025   int i, x, y, xx, yy;
9026   int new_group_nr = AmoebaNr[ax][ay];
9027   static int xy[4][2] =
9028   {
9029     { 0, -1 },
9030     { -1, 0 },
9031     { +1, 0 },
9032     { 0, +1 }
9033   };
9034
9035   if (new_group_nr == 0)
9036     return;
9037
9038   for (i = 0; i < NUM_DIRECTIONS; i++)
9039   {
9040     x = ax + xy[i][0];
9041     y = ay + xy[i][1];
9042
9043     if (!IN_LEV_FIELD(x, y))
9044       continue;
9045
9046     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9047          Feld[x][y] == EL_BD_AMOEBA ||
9048          Feld[x][y] == EL_AMOEBA_DEAD) &&
9049         AmoebaNr[x][y] != new_group_nr)
9050     {
9051       int old_group_nr = AmoebaNr[x][y];
9052
9053       if (old_group_nr == 0)
9054         return;
9055
9056       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9057       AmoebaCnt[old_group_nr] = 0;
9058       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9059       AmoebaCnt2[old_group_nr] = 0;
9060
9061       SCAN_PLAYFIELD(xx, yy)
9062       {
9063         if (AmoebaNr[xx][yy] == old_group_nr)
9064           AmoebaNr[xx][yy] = new_group_nr;
9065       }
9066     }
9067   }
9068 }
9069
9070 void AmoebeUmwandeln(int ax, int ay)
9071 {
9072   int i, x, y;
9073
9074   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9075   {
9076     int group_nr = AmoebaNr[ax][ay];
9077
9078 #ifdef DEBUG
9079     if (group_nr == 0)
9080     {
9081       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9082       printf("AmoebeUmwandeln(): This should never happen!\n");
9083       return;
9084     }
9085 #endif
9086
9087     SCAN_PLAYFIELD(x, y)
9088     {
9089       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9090       {
9091         AmoebaNr[x][y] = 0;
9092         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9093       }
9094     }
9095
9096     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9097                             SND_AMOEBA_TURNING_TO_GEM :
9098                             SND_AMOEBA_TURNING_TO_ROCK));
9099     Bang(ax, ay);
9100   }
9101   else
9102   {
9103     static int xy[4][2] =
9104     {
9105       { 0, -1 },
9106       { -1, 0 },
9107       { +1, 0 },
9108       { 0, +1 }
9109     };
9110
9111     for (i = 0; i < NUM_DIRECTIONS; i++)
9112     {
9113       x = ax + xy[i][0];
9114       y = ay + xy[i][1];
9115
9116       if (!IN_LEV_FIELD(x, y))
9117         continue;
9118
9119       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9120       {
9121         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9122                               SND_AMOEBA_TURNING_TO_GEM :
9123                               SND_AMOEBA_TURNING_TO_ROCK));
9124         Bang(x, y);
9125       }
9126     }
9127   }
9128 }
9129
9130 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9131 {
9132   int x, y;
9133   int group_nr = AmoebaNr[ax][ay];
9134   boolean done = FALSE;
9135
9136 #ifdef DEBUG
9137   if (group_nr == 0)
9138   {
9139     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9140     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9141     return;
9142   }
9143 #endif
9144
9145   SCAN_PLAYFIELD(x, y)
9146   {
9147     if (AmoebaNr[x][y] == group_nr &&
9148         (Feld[x][y] == EL_AMOEBA_DEAD ||
9149          Feld[x][y] == EL_BD_AMOEBA ||
9150          Feld[x][y] == EL_AMOEBA_GROWING))
9151     {
9152       AmoebaNr[x][y] = 0;
9153       Feld[x][y] = new_element;
9154       InitField(x, y, FALSE);
9155       TEST_DrawLevelField(x, y);
9156       done = TRUE;
9157     }
9158   }
9159
9160   if (done)
9161     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9162                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9163                             SND_BD_AMOEBA_TURNING_TO_GEM));
9164 }
9165
9166 void AmoebeWaechst(int x, int y)
9167 {
9168   static unsigned long sound_delay = 0;
9169   static unsigned long sound_delay_value = 0;
9170
9171   if (!MovDelay[x][y])          /* start new growing cycle */
9172   {
9173     MovDelay[x][y] = 7;
9174
9175     if (DelayReached(&sound_delay, sound_delay_value))
9176     {
9177       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9178       sound_delay_value = 30;
9179     }
9180   }
9181
9182   if (MovDelay[x][y])           /* wait some time before growing bigger */
9183   {
9184     MovDelay[x][y]--;
9185     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9186     {
9187       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9188                                            6 - MovDelay[x][y]);
9189
9190       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9191     }
9192
9193     if (!MovDelay[x][y])
9194     {
9195       Feld[x][y] = Store[x][y];
9196       Store[x][y] = 0;
9197       TEST_DrawLevelField(x, y);
9198     }
9199   }
9200 }
9201
9202 void AmoebaDisappearing(int x, int y)
9203 {
9204   static unsigned long sound_delay = 0;
9205   static unsigned long sound_delay_value = 0;
9206
9207   if (!MovDelay[x][y])          /* start new shrinking cycle */
9208   {
9209     MovDelay[x][y] = 7;
9210
9211     if (DelayReached(&sound_delay, sound_delay_value))
9212       sound_delay_value = 30;
9213   }
9214
9215   if (MovDelay[x][y])           /* wait some time before shrinking */
9216   {
9217     MovDelay[x][y]--;
9218     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9219     {
9220       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9221                                            6 - MovDelay[x][y]);
9222
9223       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9224     }
9225
9226     if (!MovDelay[x][y])
9227     {
9228       Feld[x][y] = EL_EMPTY;
9229       TEST_DrawLevelField(x, y);
9230
9231       /* don't let mole enter this field in this cycle;
9232          (give priority to objects falling to this field from above) */
9233       Stop[x][y] = TRUE;
9234     }
9235   }
9236 }
9237
9238 void AmoebeAbleger(int ax, int ay)
9239 {
9240   int i;
9241   int element = Feld[ax][ay];
9242   int graphic = el2img(element);
9243   int newax = ax, neway = ay;
9244   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9245   static int xy[4][2] =
9246   {
9247     { 0, -1 },
9248     { -1, 0 },
9249     { +1, 0 },
9250     { 0, +1 }
9251   };
9252
9253   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9254   {
9255     Feld[ax][ay] = EL_AMOEBA_DEAD;
9256     TEST_DrawLevelField(ax, ay);
9257     return;
9258   }
9259
9260   if (IS_ANIMATED(graphic))
9261     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9262
9263   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9264     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9265
9266   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9267   {
9268     MovDelay[ax][ay]--;
9269     if (MovDelay[ax][ay])
9270       return;
9271   }
9272
9273   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9274   {
9275     int start = RND(4);
9276     int x = ax + xy[start][0];
9277     int y = ay + xy[start][1];
9278
9279     if (!IN_LEV_FIELD(x, y))
9280       return;
9281
9282     if (IS_FREE(x, y) ||
9283         CAN_GROW_INTO(Feld[x][y]) ||
9284         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9285         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9286     {
9287       newax = x;
9288       neway = y;
9289     }
9290
9291     if (newax == ax && neway == ay)
9292       return;
9293   }
9294   else                          /* normal or "filled" (BD style) amoeba */
9295   {
9296     int start = RND(4);
9297     boolean waiting_for_player = FALSE;
9298
9299     for (i = 0; i < NUM_DIRECTIONS; i++)
9300     {
9301       int j = (start + i) % 4;
9302       int x = ax + xy[j][0];
9303       int y = ay + xy[j][1];
9304
9305       if (!IN_LEV_FIELD(x, y))
9306         continue;
9307
9308       if (IS_FREE(x, y) ||
9309           CAN_GROW_INTO(Feld[x][y]) ||
9310           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9311           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9312       {
9313         newax = x;
9314         neway = y;
9315         break;
9316       }
9317       else if (IS_PLAYER(x, y))
9318         waiting_for_player = TRUE;
9319     }
9320
9321     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9322     {
9323       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9324       {
9325         Feld[ax][ay] = EL_AMOEBA_DEAD;
9326         TEST_DrawLevelField(ax, ay);
9327         AmoebaCnt[AmoebaNr[ax][ay]]--;
9328
9329         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9330         {
9331           if (element == EL_AMOEBA_FULL)
9332             AmoebeUmwandeln(ax, ay);
9333           else if (element == EL_BD_AMOEBA)
9334             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9335         }
9336       }
9337       return;
9338     }
9339     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9340     {
9341       /* amoeba gets larger by growing in some direction */
9342
9343       int new_group_nr = AmoebaNr[ax][ay];
9344
9345 #ifdef DEBUG
9346   if (new_group_nr == 0)
9347   {
9348     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9349     printf("AmoebeAbleger(): This should never happen!\n");
9350     return;
9351   }
9352 #endif
9353
9354       AmoebaNr[newax][neway] = new_group_nr;
9355       AmoebaCnt[new_group_nr]++;
9356       AmoebaCnt2[new_group_nr]++;
9357
9358       /* if amoeba touches other amoeba(s) after growing, unify them */
9359       AmoebenVereinigen(newax, neway);
9360
9361       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9362       {
9363         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9364         return;
9365       }
9366     }
9367   }
9368
9369   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9370       (neway == lev_fieldy - 1 && newax != ax))
9371   {
9372     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9373     Store[newax][neway] = element;
9374   }
9375   else if (neway == ay || element == EL_EMC_DRIPPER)
9376   {
9377     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9378
9379     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9380   }
9381   else
9382   {
9383     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9384     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9385     Store[ax][ay] = EL_AMOEBA_DROP;
9386     ContinueMoving(ax, ay);
9387     return;
9388   }
9389
9390   TEST_DrawLevelField(newax, neway);
9391 }
9392
9393 void Life(int ax, int ay)
9394 {
9395   int x1, y1, x2, y2;
9396   int life_time = 40;
9397   int element = Feld[ax][ay];
9398   int graphic = el2img(element);
9399   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9400                          level.biomaze);
9401   boolean changed = FALSE;
9402
9403   if (IS_ANIMATED(graphic))
9404     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9405
9406   if (Stop[ax][ay])
9407     return;
9408
9409   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9410     MovDelay[ax][ay] = life_time;
9411
9412   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9413   {
9414     MovDelay[ax][ay]--;
9415     if (MovDelay[ax][ay])
9416       return;
9417   }
9418
9419   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9420   {
9421     int xx = ax+x1, yy = ay+y1;
9422     int nachbarn = 0;
9423
9424     if (!IN_LEV_FIELD(xx, yy))
9425       continue;
9426
9427     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9428     {
9429       int x = xx+x2, y = yy+y2;
9430
9431       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9432         continue;
9433
9434       if (((Feld[x][y] == element ||
9435             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9436            !Stop[x][y]) ||
9437           (IS_FREE(x, y) && Stop[x][y]))
9438         nachbarn++;
9439     }
9440
9441     if (xx == ax && yy == ay)           /* field in the middle */
9442     {
9443       if (nachbarn < life_parameter[0] ||
9444           nachbarn > life_parameter[1])
9445       {
9446         Feld[xx][yy] = EL_EMPTY;
9447         if (!Stop[xx][yy])
9448           TEST_DrawLevelField(xx, yy);
9449         Stop[xx][yy] = TRUE;
9450         changed = TRUE;
9451       }
9452     }
9453     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9454     {                                   /* free border field */
9455       if (nachbarn >= life_parameter[2] &&
9456           nachbarn <= life_parameter[3])
9457       {
9458         Feld[xx][yy] = element;
9459         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9460         if (!Stop[xx][yy])
9461           TEST_DrawLevelField(xx, yy);
9462         Stop[xx][yy] = TRUE;
9463         changed = TRUE;
9464       }
9465     }
9466   }
9467
9468   if (changed)
9469     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9470                    SND_GAME_OF_LIFE_GROWING);
9471 }
9472
9473 static void InitRobotWheel(int x, int y)
9474 {
9475   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9476 }
9477
9478 static void RunRobotWheel(int x, int y)
9479 {
9480   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9481 }
9482
9483 static void StopRobotWheel(int x, int y)
9484 {
9485   if (ZX == x && ZY == y)
9486   {
9487     ZX = ZY = -1;
9488
9489     game.robot_wheel_active = FALSE;
9490   }
9491 }
9492
9493 static void InitTimegateWheel(int x, int y)
9494 {
9495   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9496 }
9497
9498 static void RunTimegateWheel(int x, int y)
9499 {
9500   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9501 }
9502
9503 static void InitMagicBallDelay(int x, int y)
9504 {
9505 #if 1
9506   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9507 #else
9508   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9509 #endif
9510 }
9511
9512 static void ActivateMagicBall(int bx, int by)
9513 {
9514   int x, y;
9515
9516   if (level.ball_random)
9517   {
9518     int pos_border = RND(8);    /* select one of the eight border elements */
9519     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9520     int xx = pos_content % 3;
9521     int yy = pos_content / 3;
9522
9523     x = bx - 1 + xx;
9524     y = by - 1 + yy;
9525
9526     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9527       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9528   }
9529   else
9530   {
9531     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9532     {
9533       int xx = x - bx + 1;
9534       int yy = y - by + 1;
9535
9536       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9537         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9538     }
9539   }
9540
9541   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9542 }
9543
9544 void CheckExit(int x, int y)
9545 {
9546   if (local_player->gems_still_needed > 0 ||
9547       local_player->sokobanfields_still_needed > 0 ||
9548       local_player->lights_still_needed > 0)
9549   {
9550     int element = Feld[x][y];
9551     int graphic = el2img(element);
9552
9553     if (IS_ANIMATED(graphic))
9554       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9555
9556     return;
9557   }
9558
9559   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9560     return;
9561
9562   Feld[x][y] = EL_EXIT_OPENING;
9563
9564   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9565 }
9566
9567 void CheckExitEM(int x, int y)
9568 {
9569   if (local_player->gems_still_needed > 0 ||
9570       local_player->sokobanfields_still_needed > 0 ||
9571       local_player->lights_still_needed > 0)
9572   {
9573     int element = Feld[x][y];
9574     int graphic = el2img(element);
9575
9576     if (IS_ANIMATED(graphic))
9577       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9578
9579     return;
9580   }
9581
9582   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9583     return;
9584
9585   Feld[x][y] = EL_EM_EXIT_OPENING;
9586
9587   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9588 }
9589
9590 void CheckExitSteel(int x, int y)
9591 {
9592   if (local_player->gems_still_needed > 0 ||
9593       local_player->sokobanfields_still_needed > 0 ||
9594       local_player->lights_still_needed > 0)
9595   {
9596     int element = Feld[x][y];
9597     int graphic = el2img(element);
9598
9599     if (IS_ANIMATED(graphic))
9600       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9601
9602     return;
9603   }
9604
9605   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9606     return;
9607
9608   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9609
9610   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9611 }
9612
9613 void CheckExitSteelEM(int x, int y)
9614 {
9615   if (local_player->gems_still_needed > 0 ||
9616       local_player->sokobanfields_still_needed > 0 ||
9617       local_player->lights_still_needed > 0)
9618   {
9619     int element = Feld[x][y];
9620     int graphic = el2img(element);
9621
9622     if (IS_ANIMATED(graphic))
9623       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9624
9625     return;
9626   }
9627
9628   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9629     return;
9630
9631   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9632
9633   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9634 }
9635
9636 void CheckExitSP(int x, int y)
9637 {
9638   if (local_player->gems_still_needed > 0)
9639   {
9640     int element = Feld[x][y];
9641     int graphic = el2img(element);
9642
9643     if (IS_ANIMATED(graphic))
9644       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9645
9646     return;
9647   }
9648
9649   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9650     return;
9651
9652   Feld[x][y] = EL_SP_EXIT_OPENING;
9653
9654   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9655 }
9656
9657 static void CloseAllOpenTimegates()
9658 {
9659   int x, y;
9660
9661   SCAN_PLAYFIELD(x, y)
9662   {
9663     int element = Feld[x][y];
9664
9665     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9666     {
9667       Feld[x][y] = EL_TIMEGATE_CLOSING;
9668
9669       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9670     }
9671   }
9672 }
9673
9674 void DrawTwinkleOnField(int x, int y)
9675 {
9676   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9677     return;
9678
9679   if (Feld[x][y] == EL_BD_DIAMOND)
9680     return;
9681
9682   if (MovDelay[x][y] == 0)      /* next animation frame */
9683     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9684
9685   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9686   {
9687     MovDelay[x][y]--;
9688
9689     DrawLevelElementAnimation(x, y, Feld[x][y]);
9690
9691     if (MovDelay[x][y] != 0)
9692     {
9693       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9694                                            10 - MovDelay[x][y]);
9695
9696       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9697     }
9698   }
9699 }
9700
9701 void MauerWaechst(int x, int y)
9702 {
9703   int delay = 6;
9704
9705   if (!MovDelay[x][y])          /* next animation frame */
9706     MovDelay[x][y] = 3 * delay;
9707
9708   if (MovDelay[x][y])           /* wait some time before next frame */
9709   {
9710     MovDelay[x][y]--;
9711
9712     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9713     {
9714       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9715       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9716
9717       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9718     }
9719
9720     if (!MovDelay[x][y])
9721     {
9722       if (MovDir[x][y] == MV_LEFT)
9723       {
9724         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9725           TEST_DrawLevelField(x - 1, y);
9726       }
9727       else if (MovDir[x][y] == MV_RIGHT)
9728       {
9729         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9730           TEST_DrawLevelField(x + 1, y);
9731       }
9732       else if (MovDir[x][y] == MV_UP)
9733       {
9734         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9735           TEST_DrawLevelField(x, y - 1);
9736       }
9737       else
9738       {
9739         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9740           TEST_DrawLevelField(x, y + 1);
9741       }
9742
9743       Feld[x][y] = Store[x][y];
9744       Store[x][y] = 0;
9745       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9746       TEST_DrawLevelField(x, y);
9747     }
9748   }
9749 }
9750
9751 void MauerAbleger(int ax, int ay)
9752 {
9753   int element = Feld[ax][ay];
9754   int graphic = el2img(element);
9755   boolean oben_frei = FALSE, unten_frei = FALSE;
9756   boolean links_frei = FALSE, rechts_frei = FALSE;
9757   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9758   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9759   boolean new_wall = FALSE;
9760
9761   if (IS_ANIMATED(graphic))
9762     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9763
9764   if (!MovDelay[ax][ay])        /* start building new wall */
9765     MovDelay[ax][ay] = 6;
9766
9767   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9768   {
9769     MovDelay[ax][ay]--;
9770     if (MovDelay[ax][ay])
9771       return;
9772   }
9773
9774   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9775     oben_frei = TRUE;
9776   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9777     unten_frei = TRUE;
9778   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9779     links_frei = TRUE;
9780   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9781     rechts_frei = TRUE;
9782
9783   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9784       element == EL_EXPANDABLE_WALL_ANY)
9785   {
9786     if (oben_frei)
9787     {
9788       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9789       Store[ax][ay-1] = element;
9790       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9791       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9792         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9793                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9794       new_wall = TRUE;
9795     }
9796     if (unten_frei)
9797     {
9798       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9799       Store[ax][ay+1] = element;
9800       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9801       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9802         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9803                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9804       new_wall = TRUE;
9805     }
9806   }
9807
9808   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9809       element == EL_EXPANDABLE_WALL_ANY ||
9810       element == EL_EXPANDABLE_WALL ||
9811       element == EL_BD_EXPANDABLE_WALL)
9812   {
9813     if (links_frei)
9814     {
9815       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9816       Store[ax-1][ay] = element;
9817       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9818       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9819         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9820                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9821       new_wall = TRUE;
9822     }
9823
9824     if (rechts_frei)
9825     {
9826       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9827       Store[ax+1][ay] = element;
9828       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9829       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9830         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9831                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9832       new_wall = TRUE;
9833     }
9834   }
9835
9836   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9837     TEST_DrawLevelField(ax, ay);
9838
9839   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9840     oben_massiv = TRUE;
9841   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9842     unten_massiv = TRUE;
9843   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9844     links_massiv = TRUE;
9845   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9846     rechts_massiv = TRUE;
9847
9848   if (((oben_massiv && unten_massiv) ||
9849        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9850        element == EL_EXPANDABLE_WALL) &&
9851       ((links_massiv && rechts_massiv) ||
9852        element == EL_EXPANDABLE_WALL_VERTICAL))
9853     Feld[ax][ay] = EL_WALL;
9854
9855   if (new_wall)
9856     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9857 }
9858
9859 void MauerAblegerStahl(int ax, int ay)
9860 {
9861   int element = Feld[ax][ay];
9862   int graphic = el2img(element);
9863   boolean oben_frei = FALSE, unten_frei = FALSE;
9864   boolean links_frei = FALSE, rechts_frei = FALSE;
9865   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9866   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9867   boolean new_wall = FALSE;
9868
9869   if (IS_ANIMATED(graphic))
9870     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9871
9872   if (!MovDelay[ax][ay])        /* start building new wall */
9873     MovDelay[ax][ay] = 6;
9874
9875   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9876   {
9877     MovDelay[ax][ay]--;
9878     if (MovDelay[ax][ay])
9879       return;
9880   }
9881
9882   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9883     oben_frei = TRUE;
9884   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9885     unten_frei = TRUE;
9886   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9887     links_frei = TRUE;
9888   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9889     rechts_frei = TRUE;
9890
9891   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9892       element == EL_EXPANDABLE_STEELWALL_ANY)
9893   {
9894     if (oben_frei)
9895     {
9896       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9897       Store[ax][ay-1] = element;
9898       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9899       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9900         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9901                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9902       new_wall = TRUE;
9903     }
9904     if (unten_frei)
9905     {
9906       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9907       Store[ax][ay+1] = element;
9908       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9909       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9910         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9911                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9912       new_wall = TRUE;
9913     }
9914   }
9915
9916   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9917       element == EL_EXPANDABLE_STEELWALL_ANY)
9918   {
9919     if (links_frei)
9920     {
9921       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9922       Store[ax-1][ay] = element;
9923       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9924       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9925         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9926                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9927       new_wall = TRUE;
9928     }
9929
9930     if (rechts_frei)
9931     {
9932       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9933       Store[ax+1][ay] = element;
9934       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9935       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9936         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9937                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9938       new_wall = TRUE;
9939     }
9940   }
9941
9942   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9943     oben_massiv = TRUE;
9944   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9945     unten_massiv = TRUE;
9946   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9947     links_massiv = TRUE;
9948   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9949     rechts_massiv = TRUE;
9950
9951   if (((oben_massiv && unten_massiv) ||
9952        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9953       ((links_massiv && rechts_massiv) ||
9954        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9955     Feld[ax][ay] = EL_STEELWALL;
9956
9957   if (new_wall)
9958     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9959 }
9960
9961 void CheckForDragon(int x, int y)
9962 {
9963   int i, j;
9964   boolean dragon_found = FALSE;
9965   static int xy[4][2] =
9966   {
9967     { 0, -1 },
9968     { -1, 0 },
9969     { +1, 0 },
9970     { 0, +1 }
9971   };
9972
9973   for (i = 0; i < NUM_DIRECTIONS; i++)
9974   {
9975     for (j = 0; j < 4; j++)
9976     {
9977       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9978
9979       if (IN_LEV_FIELD(xx, yy) &&
9980           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9981       {
9982         if (Feld[xx][yy] == EL_DRAGON)
9983           dragon_found = TRUE;
9984       }
9985       else
9986         break;
9987     }
9988   }
9989
9990   if (!dragon_found)
9991   {
9992     for (i = 0; i < NUM_DIRECTIONS; i++)
9993     {
9994       for (j = 0; j < 3; j++)
9995       {
9996         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9997   
9998         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9999         {
10000           Feld[xx][yy] = EL_EMPTY;
10001           TEST_DrawLevelField(xx, yy);
10002         }
10003         else
10004           break;
10005       }
10006     }
10007   }
10008 }
10009
10010 static void InitBuggyBase(int x, int y)
10011 {
10012   int element = Feld[x][y];
10013   int activating_delay = FRAMES_PER_SECOND / 4;
10014
10015   ChangeDelay[x][y] =
10016     (element == EL_SP_BUGGY_BASE ?
10017      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10018      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10019      activating_delay :
10020      element == EL_SP_BUGGY_BASE_ACTIVE ?
10021      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10022 }
10023
10024 static void WarnBuggyBase(int x, int y)
10025 {
10026   int i;
10027   static int xy[4][2] =
10028   {
10029     { 0, -1 },
10030     { -1, 0 },
10031     { +1, 0 },
10032     { 0, +1 }
10033   };
10034
10035   for (i = 0; i < NUM_DIRECTIONS; i++)
10036   {
10037     int xx = x + xy[i][0];
10038     int yy = y + xy[i][1];
10039
10040     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10041     {
10042       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10043
10044       break;
10045     }
10046   }
10047 }
10048
10049 static void InitTrap(int x, int y)
10050 {
10051   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10052 }
10053
10054 static void ActivateTrap(int x, int y)
10055 {
10056   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10057 }
10058
10059 static void ChangeActiveTrap(int x, int y)
10060 {
10061   int graphic = IMG_TRAP_ACTIVE;
10062
10063   /* if new animation frame was drawn, correct crumbled sand border */
10064   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10065     TEST_DrawLevelFieldCrumbledSand(x, y);
10066 }
10067
10068 static int getSpecialActionElement(int element, int number, int base_element)
10069 {
10070   return (element != EL_EMPTY ? element :
10071           number != -1 ? base_element + number - 1 :
10072           EL_EMPTY);
10073 }
10074
10075 static int getModifiedActionNumber(int value_old, int operator, int operand,
10076                                    int value_min, int value_max)
10077 {
10078   int value_new = (operator == CA_MODE_SET      ? operand :
10079                    operator == CA_MODE_ADD      ? value_old + operand :
10080                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10081                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10082                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10083                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10084                    value_old);
10085
10086   return (value_new < value_min ? value_min :
10087           value_new > value_max ? value_max :
10088           value_new);
10089 }
10090
10091 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10092 {
10093   struct ElementInfo *ei = &element_info[element];
10094   struct ElementChangeInfo *change = &ei->change_page[page];
10095   int target_element = change->target_element;
10096   int action_type = change->action_type;
10097   int action_mode = change->action_mode;
10098   int action_arg = change->action_arg;
10099   int i;
10100
10101   if (!change->has_action)
10102     return;
10103
10104   /* ---------- determine action paramater values -------------------------- */
10105
10106   int level_time_value =
10107     (level.time > 0 ? TimeLeft :
10108      TimePlayed);
10109
10110   int action_arg_element =
10111     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10112      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10113      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10114      EL_EMPTY);
10115
10116   int action_arg_direction =
10117     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10118      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10119      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10120      change->actual_trigger_side :
10121      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10122      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10123      MV_NONE);
10124
10125   int action_arg_number_min =
10126     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10127      CA_ARG_MIN);
10128
10129   int action_arg_number_max =
10130     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10131      action_type == CA_SET_LEVEL_GEMS ? 999 :
10132      action_type == CA_SET_LEVEL_TIME ? 9999 :
10133      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10134      action_type == CA_SET_CE_VALUE ? 9999 :
10135      action_type == CA_SET_CE_SCORE ? 9999 :
10136      CA_ARG_MAX);
10137
10138   int action_arg_number_reset =
10139     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10140      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10141      action_type == CA_SET_LEVEL_TIME ? level.time :
10142      action_type == CA_SET_LEVEL_SCORE ? 0 :
10143 #if USE_NEW_CUSTOM_VALUE
10144      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10145 #else
10146      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10147 #endif
10148      action_type == CA_SET_CE_SCORE ? 0 :
10149      0);
10150
10151   int action_arg_number =
10152     (action_arg <= CA_ARG_MAX ? action_arg :
10153      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10154      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10155      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10156      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10157      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10158      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10159 #if USE_NEW_CUSTOM_VALUE
10160      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10161 #else
10162      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10163 #endif
10164      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10165      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10166      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10167      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10168      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10169      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10170      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10171      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10172      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10173      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10174      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10175      -1);
10176
10177   int action_arg_number_old =
10178     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10179      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10180      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10181      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10182      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10183      0);
10184
10185   int action_arg_number_new =
10186     getModifiedActionNumber(action_arg_number_old,
10187                             action_mode, action_arg_number,
10188                             action_arg_number_min, action_arg_number_max);
10189
10190 #if 1
10191   int trigger_player_bits = change->actual_trigger_player_bits;
10192 #else
10193   int trigger_player_bits =
10194     (change->actual_trigger_player >= EL_PLAYER_1 &&
10195      change->actual_trigger_player <= EL_PLAYER_4 ?
10196      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10197      PLAYER_BITS_ANY);
10198 #endif
10199
10200   int action_arg_player_bits =
10201     (action_arg >= CA_ARG_PLAYER_1 &&
10202      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10203      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10204      PLAYER_BITS_ANY);
10205
10206   /* ---------- execute action  -------------------------------------------- */
10207
10208   switch (action_type)
10209   {
10210     case CA_NO_ACTION:
10211     {
10212       return;
10213     }
10214
10215     /* ---------- level actions  ------------------------------------------- */
10216
10217     case CA_RESTART_LEVEL:
10218     {
10219       game.restart_level = TRUE;
10220
10221       break;
10222     }
10223
10224     case CA_SHOW_ENVELOPE:
10225     {
10226       int element = getSpecialActionElement(action_arg_element,
10227                                             action_arg_number, EL_ENVELOPE_1);
10228
10229       if (IS_ENVELOPE(element))
10230         local_player->show_envelope = element;
10231
10232       break;
10233     }
10234
10235     case CA_SET_LEVEL_TIME:
10236     {
10237       if (level.time > 0)       /* only modify limited time value */
10238       {
10239         TimeLeft = action_arg_number_new;
10240
10241 #if 1
10242         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10243
10244         DisplayGameControlValues();
10245 #else
10246         DrawGameValue_Time(TimeLeft);
10247 #endif
10248
10249         if (!TimeLeft && setup.time_limit)
10250           for (i = 0; i < MAX_PLAYERS; i++)
10251             KillPlayer(&stored_player[i]);
10252       }
10253
10254       break;
10255     }
10256
10257     case CA_SET_LEVEL_SCORE:
10258     {
10259       local_player->score = action_arg_number_new;
10260
10261 #if 1
10262       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10263
10264       DisplayGameControlValues();
10265 #else
10266       DrawGameValue_Score(local_player->score);
10267 #endif
10268
10269       break;
10270     }
10271
10272     case CA_SET_LEVEL_GEMS:
10273     {
10274       local_player->gems_still_needed = action_arg_number_new;
10275
10276 #if 1
10277       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10278
10279       DisplayGameControlValues();
10280 #else
10281       DrawGameValue_Emeralds(local_player->gems_still_needed);
10282 #endif
10283
10284       break;
10285     }
10286
10287 #if !USE_PLAYER_GRAVITY
10288     case CA_SET_LEVEL_GRAVITY:
10289     {
10290       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10291                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10292                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10293                       game.gravity);
10294       break;
10295     }
10296 #endif
10297
10298     case CA_SET_LEVEL_WIND:
10299     {
10300       game.wind_direction = action_arg_direction;
10301
10302       break;
10303     }
10304
10305     /* ---------- player actions  ------------------------------------------ */
10306
10307     case CA_MOVE_PLAYER:
10308     {
10309       /* automatically move to the next field in specified direction */
10310       for (i = 0; i < MAX_PLAYERS; i++)
10311         if (trigger_player_bits & (1 << i))
10312           stored_player[i].programmed_action = action_arg_direction;
10313
10314       break;
10315     }
10316
10317     case CA_EXIT_PLAYER:
10318     {
10319       for (i = 0; i < MAX_PLAYERS; i++)
10320         if (action_arg_player_bits & (1 << i))
10321           PlayerWins(&stored_player[i]);
10322
10323       break;
10324     }
10325
10326     case CA_KILL_PLAYER:
10327     {
10328       for (i = 0; i < MAX_PLAYERS; i++)
10329         if (action_arg_player_bits & (1 << i))
10330           KillPlayer(&stored_player[i]);
10331
10332       break;
10333     }
10334
10335     case CA_SET_PLAYER_KEYS:
10336     {
10337       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10338       int element = getSpecialActionElement(action_arg_element,
10339                                             action_arg_number, EL_KEY_1);
10340
10341       if (IS_KEY(element))
10342       {
10343         for (i = 0; i < MAX_PLAYERS; i++)
10344         {
10345           if (trigger_player_bits & (1 << i))
10346           {
10347             stored_player[i].key[KEY_NR(element)] = key_state;
10348
10349             DrawGameDoorValues();
10350           }
10351         }
10352       }
10353
10354       break;
10355     }
10356
10357     case CA_SET_PLAYER_SPEED:
10358     {
10359       for (i = 0; i < MAX_PLAYERS; i++)
10360       {
10361         if (trigger_player_bits & (1 << i))
10362         {
10363           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10364
10365           if (action_arg == CA_ARG_SPEED_FASTER &&
10366               stored_player[i].cannot_move)
10367           {
10368             action_arg_number = STEPSIZE_VERY_SLOW;
10369           }
10370           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10371                    action_arg == CA_ARG_SPEED_FASTER)
10372           {
10373             action_arg_number = 2;
10374             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10375                            CA_MODE_MULTIPLY);
10376           }
10377           else if (action_arg == CA_ARG_NUMBER_RESET)
10378           {
10379             action_arg_number = level.initial_player_stepsize[i];
10380           }
10381
10382           move_stepsize =
10383             getModifiedActionNumber(move_stepsize,
10384                                     action_mode,
10385                                     action_arg_number,
10386                                     action_arg_number_min,
10387                                     action_arg_number_max);
10388
10389           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10390         }
10391       }
10392
10393       break;
10394     }
10395
10396     case CA_SET_PLAYER_SHIELD:
10397     {
10398       for (i = 0; i < MAX_PLAYERS; i++)
10399       {
10400         if (trigger_player_bits & (1 << i))
10401         {
10402           if (action_arg == CA_ARG_SHIELD_OFF)
10403           {
10404             stored_player[i].shield_normal_time_left = 0;
10405             stored_player[i].shield_deadly_time_left = 0;
10406           }
10407           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10408           {
10409             stored_player[i].shield_normal_time_left = 999999;
10410           }
10411           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10412           {
10413             stored_player[i].shield_normal_time_left = 999999;
10414             stored_player[i].shield_deadly_time_left = 999999;
10415           }
10416         }
10417       }
10418
10419       break;
10420     }
10421
10422 #if USE_PLAYER_GRAVITY
10423     case CA_SET_PLAYER_GRAVITY:
10424     {
10425       for (i = 0; i < MAX_PLAYERS; i++)
10426       {
10427         if (trigger_player_bits & (1 << i))
10428         {
10429           stored_player[i].gravity =
10430             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10431              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10432              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10433              stored_player[i].gravity);
10434         }
10435       }
10436
10437       break;
10438     }
10439 #endif
10440
10441     case CA_SET_PLAYER_ARTWORK:
10442     {
10443       for (i = 0; i < MAX_PLAYERS; i++)
10444       {
10445         if (trigger_player_bits & (1 << i))
10446         {
10447           int artwork_element = action_arg_element;
10448
10449           if (action_arg == CA_ARG_ELEMENT_RESET)
10450             artwork_element =
10451               (level.use_artwork_element[i] ? level.artwork_element[i] :
10452                stored_player[i].element_nr);
10453
10454 #if USE_GFX_RESET_PLAYER_ARTWORK
10455           if (stored_player[i].artwork_element != artwork_element)
10456             stored_player[i].Frame = 0;
10457 #endif
10458
10459           stored_player[i].artwork_element = artwork_element;
10460
10461           SetPlayerWaiting(&stored_player[i], FALSE);
10462
10463           /* set number of special actions for bored and sleeping animation */
10464           stored_player[i].num_special_action_bored =
10465             get_num_special_action(artwork_element,
10466                                    ACTION_BORING_1, ACTION_BORING_LAST);
10467           stored_player[i].num_special_action_sleeping =
10468             get_num_special_action(artwork_element,
10469                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10470         }
10471       }
10472
10473       break;
10474     }
10475
10476     /* ---------- CE actions  ---------------------------------------------- */
10477
10478     case CA_SET_CE_VALUE:
10479     {
10480 #if USE_NEW_CUSTOM_VALUE
10481       int last_ce_value = CustomValue[x][y];
10482
10483       CustomValue[x][y] = action_arg_number_new;
10484
10485       if (CustomValue[x][y] != last_ce_value)
10486       {
10487         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10488         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10489
10490         if (CustomValue[x][y] == 0)
10491         {
10492           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10493           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10494         }
10495       }
10496 #endif
10497
10498       break;
10499     }
10500
10501     case CA_SET_CE_SCORE:
10502     {
10503 #if USE_NEW_CUSTOM_VALUE
10504       int last_ce_score = ei->collect_score;
10505
10506       ei->collect_score = action_arg_number_new;
10507
10508       if (ei->collect_score != last_ce_score)
10509       {
10510         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10511         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10512
10513         if (ei->collect_score == 0)
10514         {
10515           int xx, yy;
10516
10517           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10518           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10519
10520           /*
10521             This is a very special case that seems to be a mixture between
10522             CheckElementChange() and CheckTriggeredElementChange(): while
10523             the first one only affects single elements that are triggered
10524             directly, the second one affects multiple elements in the playfield
10525             that are triggered indirectly by another element. This is a third
10526             case: Changing the CE score always affects multiple identical CEs,
10527             so every affected CE must be checked, not only the single CE for
10528             which the CE score was changed in the first place (as every instance
10529             of that CE shares the same CE score, and therefore also can change)!
10530           */
10531           SCAN_PLAYFIELD(xx, yy)
10532           {
10533             if (Feld[xx][yy] == element)
10534               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10535                                  CE_SCORE_GETS_ZERO);
10536           }
10537         }
10538       }
10539 #endif
10540
10541       break;
10542     }
10543
10544     /* ---------- engine actions  ------------------------------------------ */
10545
10546     case CA_SET_ENGINE_SCAN_MODE:
10547     {
10548       InitPlayfieldScanMode(action_arg);
10549
10550       break;
10551     }
10552
10553     default:
10554       break;
10555   }
10556 }
10557
10558 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10559 {
10560   int old_element = Feld[x][y];
10561   int new_element = GetElementFromGroupElement(element);
10562   int previous_move_direction = MovDir[x][y];
10563 #if USE_NEW_CUSTOM_VALUE
10564   int last_ce_value = CustomValue[x][y];
10565 #endif
10566   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10567   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10568   boolean add_player_onto_element = (new_element_is_player &&
10569 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10570                                      /* this breaks SnakeBite when a snake is
10571                                         halfway through a door that closes */
10572                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10573                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10574 #endif
10575                                      IS_WALKABLE(old_element));
10576
10577 #if 0
10578   /* check if element under the player changes from accessible to unaccessible
10579      (needed for special case of dropping element which then changes) */
10580   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10581       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10582   {
10583     Bang(x, y);
10584
10585     return;
10586   }
10587 #endif
10588
10589   if (!add_player_onto_element)
10590   {
10591     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10592       RemoveMovingField(x, y);
10593     else
10594       RemoveField(x, y);
10595
10596     Feld[x][y] = new_element;
10597
10598 #if !USE_GFX_RESET_GFX_ANIMATION
10599     ResetGfxAnimation(x, y);
10600     ResetRandomAnimationValue(x, y);
10601 #endif
10602
10603     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10604       MovDir[x][y] = previous_move_direction;
10605
10606 #if USE_NEW_CUSTOM_VALUE
10607     if (element_info[new_element].use_last_ce_value)
10608       CustomValue[x][y] = last_ce_value;
10609 #endif
10610
10611     InitField_WithBug1(x, y, FALSE);
10612
10613     new_element = Feld[x][y];   /* element may have changed */
10614
10615 #if USE_GFX_RESET_GFX_ANIMATION
10616     ResetGfxAnimation(x, y);
10617     ResetRandomAnimationValue(x, y);
10618 #endif
10619
10620     TEST_DrawLevelField(x, y);
10621
10622     if (GFX_CRUMBLED(new_element))
10623       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
10624   }
10625
10626 #if 1
10627   /* check if element under the player changes from accessible to unaccessible
10628      (needed for special case of dropping element which then changes) */
10629   /* (must be checked after creating new element for walkable group elements) */
10630 #if USE_FIX_KILLED_BY_NON_WALKABLE
10631   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10632       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10633   {
10634     Bang(x, y);
10635
10636     return;
10637   }
10638 #else
10639   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10640       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10641   {
10642     Bang(x, y);
10643
10644     return;
10645   }
10646 #endif
10647 #endif
10648
10649   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10650   if (new_element_is_player)
10651     RelocatePlayer(x, y, new_element);
10652
10653   if (is_change)
10654     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10655
10656   TestIfBadThingTouchesPlayer(x, y);
10657   TestIfPlayerTouchesCustomElement(x, y);
10658   TestIfElementTouchesCustomElement(x, y);
10659 }
10660
10661 static void CreateField(int x, int y, int element)
10662 {
10663   CreateFieldExt(x, y, element, FALSE);
10664 }
10665
10666 static void CreateElementFromChange(int x, int y, int element)
10667 {
10668   element = GET_VALID_RUNTIME_ELEMENT(element);
10669
10670 #if USE_STOP_CHANGED_ELEMENTS
10671   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10672   {
10673     int old_element = Feld[x][y];
10674
10675     /* prevent changed element from moving in same engine frame
10676        unless both old and new element can either fall or move */
10677     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10678         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10679       Stop[x][y] = TRUE;
10680   }
10681 #endif
10682
10683   CreateFieldExt(x, y, element, TRUE);
10684 }
10685
10686 static boolean ChangeElement(int x, int y, int element, int page)
10687 {
10688   struct ElementInfo *ei = &element_info[element];
10689   struct ElementChangeInfo *change = &ei->change_page[page];
10690   int ce_value = CustomValue[x][y];
10691   int ce_score = ei->collect_score;
10692   int target_element;
10693   int old_element = Feld[x][y];
10694
10695   /* always use default change event to prevent running into a loop */
10696   if (ChangeEvent[x][y] == -1)
10697     ChangeEvent[x][y] = CE_DELAY;
10698
10699   if (ChangeEvent[x][y] == CE_DELAY)
10700   {
10701     /* reset actual trigger element, trigger player and action element */
10702     change->actual_trigger_element = EL_EMPTY;
10703     change->actual_trigger_player = EL_PLAYER_1;
10704     change->actual_trigger_player_bits = CH_PLAYER_1;
10705     change->actual_trigger_side = CH_SIDE_NONE;
10706     change->actual_trigger_ce_value = 0;
10707     change->actual_trigger_ce_score = 0;
10708   }
10709
10710   /* do not change elements more than a specified maximum number of changes */
10711   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10712     return FALSE;
10713
10714   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10715
10716   if (change->explode)
10717   {
10718     Bang(x, y);
10719
10720     return TRUE;
10721   }
10722
10723   if (change->use_target_content)
10724   {
10725     boolean complete_replace = TRUE;
10726     boolean can_replace[3][3];
10727     int xx, yy;
10728
10729     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10730     {
10731       boolean is_empty;
10732       boolean is_walkable;
10733       boolean is_diggable;
10734       boolean is_collectible;
10735       boolean is_removable;
10736       boolean is_destructible;
10737       int ex = x + xx - 1;
10738       int ey = y + yy - 1;
10739       int content_element = change->target_content.e[xx][yy];
10740       int e;
10741
10742       can_replace[xx][yy] = TRUE;
10743
10744       if (ex == x && ey == y)   /* do not check changing element itself */
10745         continue;
10746
10747       if (content_element == EL_EMPTY_SPACE)
10748       {
10749         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10750
10751         continue;
10752       }
10753
10754       if (!IN_LEV_FIELD(ex, ey))
10755       {
10756         can_replace[xx][yy] = FALSE;
10757         complete_replace = FALSE;
10758
10759         continue;
10760       }
10761
10762       e = Feld[ex][ey];
10763
10764       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10765         e = MovingOrBlocked2Element(ex, ey);
10766
10767       is_empty = (IS_FREE(ex, ey) ||
10768                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10769
10770       is_walkable     = (is_empty || IS_WALKABLE(e));
10771       is_diggable     = (is_empty || IS_DIGGABLE(e));
10772       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10773       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10774       is_removable    = (is_diggable || is_collectible);
10775
10776       can_replace[xx][yy] =
10777         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10778           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10779           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10780           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10781           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10782           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10783          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10784
10785       if (!can_replace[xx][yy])
10786         complete_replace = FALSE;
10787     }
10788
10789     if (!change->only_if_complete || complete_replace)
10790     {
10791       boolean something_has_changed = FALSE;
10792
10793       if (change->only_if_complete && change->use_random_replace &&
10794           RND(100) < change->random_percentage)
10795         return FALSE;
10796
10797       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10798       {
10799         int ex = x + xx - 1;
10800         int ey = y + yy - 1;
10801         int content_element;
10802
10803         if (can_replace[xx][yy] && (!change->use_random_replace ||
10804                                     RND(100) < change->random_percentage))
10805         {
10806           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10807             RemoveMovingField(ex, ey);
10808
10809           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10810
10811           content_element = change->target_content.e[xx][yy];
10812           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10813                                               ce_value, ce_score);
10814
10815           CreateElementFromChange(ex, ey, target_element);
10816
10817           something_has_changed = TRUE;
10818
10819           /* for symmetry reasons, freeze newly created border elements */
10820           if (ex != x || ey != y)
10821             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10822         }
10823       }
10824
10825       if (something_has_changed)
10826       {
10827         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10828         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10829       }
10830     }
10831   }
10832   else
10833   {
10834     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10835                                         ce_value, ce_score);
10836
10837     if (element == EL_DIAGONAL_GROWING ||
10838         element == EL_DIAGONAL_SHRINKING)
10839     {
10840       target_element = Store[x][y];
10841
10842       Store[x][y] = EL_EMPTY;
10843     }
10844
10845     CreateElementFromChange(x, y, target_element);
10846
10847     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10848     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10849   }
10850
10851   /* this uses direct change before indirect change */
10852   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10853
10854   return TRUE;
10855 }
10856
10857 #if USE_NEW_DELAYED_ACTION
10858
10859 static void HandleElementChange(int x, int y, int page)
10860 {
10861   int element = MovingOrBlocked2Element(x, y);
10862   struct ElementInfo *ei = &element_info[element];
10863   struct ElementChangeInfo *change = &ei->change_page[page];
10864
10865 #ifdef DEBUG
10866   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10867       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10868   {
10869     printf("\n\n");
10870     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10871            x, y, element, element_info[element].token_name);
10872     printf("HandleElementChange(): This should never happen!\n");
10873     printf("\n\n");
10874   }
10875 #endif
10876
10877   /* this can happen with classic bombs on walkable, changing elements */
10878   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10879   {
10880 #if 0
10881     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10882       ChangeDelay[x][y] = 0;
10883 #endif
10884
10885     return;
10886   }
10887
10888   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10889   {
10890     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10891
10892     if (change->can_change)
10893     {
10894 #if 1
10895       /* !!! not clear why graphic animation should be reset at all here !!! */
10896       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10897 #if USE_GFX_RESET_WHEN_NOT_MOVING
10898       /* when a custom element is about to change (for example by change delay),
10899          do not reset graphic animation when the custom element is moving */
10900       if (!IS_MOVING(x, y))
10901 #endif
10902       {
10903         ResetGfxAnimation(x, y);
10904         ResetRandomAnimationValue(x, y);
10905       }
10906 #endif
10907
10908       if (change->pre_change_function)
10909         change->pre_change_function(x, y);
10910     }
10911   }
10912
10913   ChangeDelay[x][y]--;
10914
10915   if (ChangeDelay[x][y] != 0)           /* continue element change */
10916   {
10917     if (change->can_change)
10918     {
10919       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10920
10921       if (IS_ANIMATED(graphic))
10922         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10923
10924       if (change->change_function)
10925         change->change_function(x, y);
10926     }
10927   }
10928   else                                  /* finish element change */
10929   {
10930     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10931     {
10932       page = ChangePage[x][y];
10933       ChangePage[x][y] = -1;
10934
10935       change = &ei->change_page[page];
10936     }
10937
10938     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10939     {
10940       ChangeDelay[x][y] = 1;            /* try change after next move step */
10941       ChangePage[x][y] = page;          /* remember page to use for change */
10942
10943       return;
10944     }
10945
10946     if (change->can_change)
10947     {
10948       if (ChangeElement(x, y, element, page))
10949       {
10950         if (change->post_change_function)
10951           change->post_change_function(x, y);
10952       }
10953     }
10954
10955     if (change->has_action)
10956       ExecuteCustomElementAction(x, y, element, page);
10957   }
10958 }
10959
10960 #else
10961
10962 static void HandleElementChange(int x, int y, int page)
10963 {
10964   int element = MovingOrBlocked2Element(x, y);
10965   struct ElementInfo *ei = &element_info[element];
10966   struct ElementChangeInfo *change = &ei->change_page[page];
10967
10968 #ifdef DEBUG
10969   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10970   {
10971     printf("\n\n");
10972     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10973            x, y, element, element_info[element].token_name);
10974     printf("HandleElementChange(): This should never happen!\n");
10975     printf("\n\n");
10976   }
10977 #endif
10978
10979   /* this can happen with classic bombs on walkable, changing elements */
10980   if (!CAN_CHANGE(element))
10981   {
10982 #if 0
10983     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10984       ChangeDelay[x][y] = 0;
10985 #endif
10986
10987     return;
10988   }
10989
10990   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10991   {
10992     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10993
10994     ResetGfxAnimation(x, y);
10995     ResetRandomAnimationValue(x, y);
10996
10997     if (change->pre_change_function)
10998       change->pre_change_function(x, y);
10999   }
11000
11001   ChangeDelay[x][y]--;
11002
11003   if (ChangeDelay[x][y] != 0)           /* continue element change */
11004   {
11005     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11006
11007     if (IS_ANIMATED(graphic))
11008       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11009
11010     if (change->change_function)
11011       change->change_function(x, y);
11012   }
11013   else                                  /* finish element change */
11014   {
11015     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11016     {
11017       page = ChangePage[x][y];
11018       ChangePage[x][y] = -1;
11019
11020       change = &ei->change_page[page];
11021     }
11022
11023     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11024     {
11025       ChangeDelay[x][y] = 1;            /* try change after next move step */
11026       ChangePage[x][y] = page;          /* remember page to use for change */
11027
11028       return;
11029     }
11030
11031     if (ChangeElement(x, y, element, page))
11032     {
11033       if (change->post_change_function)
11034         change->post_change_function(x, y);
11035     }
11036   }
11037 }
11038
11039 #endif
11040
11041 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11042                                               int trigger_element,
11043                                               int trigger_event,
11044                                               int trigger_player,
11045                                               int trigger_side,
11046                                               int trigger_page)
11047 {
11048   boolean change_done_any = FALSE;
11049   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11050   int i;
11051
11052   if (!(trigger_events[trigger_element][trigger_event]))
11053     return FALSE;
11054
11055 #if 0
11056   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11057          trigger_event, recursion_loop_depth, recursion_loop_detected,
11058          recursion_loop_element, EL_NAME(recursion_loop_element));
11059 #endif
11060
11061   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11062
11063   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11064   {
11065     int element = EL_CUSTOM_START + i;
11066     boolean change_done = FALSE;
11067     int p;
11068
11069     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11070         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11071       continue;
11072
11073     for (p = 0; p < element_info[element].num_change_pages; p++)
11074     {
11075       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11076
11077       if (change->can_change_or_has_action &&
11078           change->has_event[trigger_event] &&
11079           change->trigger_side & trigger_side &&
11080           change->trigger_player & trigger_player &&
11081           change->trigger_page & trigger_page_bits &&
11082           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11083       {
11084         change->actual_trigger_element = trigger_element;
11085         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11086         change->actual_trigger_player_bits = trigger_player;
11087         change->actual_trigger_side = trigger_side;
11088         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11089         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11090
11091         if ((change->can_change && !change_done) || change->has_action)
11092         {
11093           int x, y;
11094
11095           SCAN_PLAYFIELD(x, y)
11096           {
11097             if (Feld[x][y] == element)
11098             {
11099               if (change->can_change && !change_done)
11100               {
11101                 ChangeDelay[x][y] = 1;
11102                 ChangeEvent[x][y] = trigger_event;
11103
11104                 HandleElementChange(x, y, p);
11105               }
11106 #if USE_NEW_DELAYED_ACTION
11107               else if (change->has_action)
11108               {
11109                 ExecuteCustomElementAction(x, y, element, p);
11110                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11111               }
11112 #else
11113               if (change->has_action)
11114               {
11115                 ExecuteCustomElementAction(x, y, element, p);
11116                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11117               }
11118 #endif
11119             }
11120           }
11121
11122           if (change->can_change)
11123           {
11124             change_done = TRUE;
11125             change_done_any = TRUE;
11126           }
11127         }
11128       }
11129     }
11130   }
11131
11132   RECURSION_LOOP_DETECTION_END();
11133
11134   return change_done_any;
11135 }
11136
11137 static boolean CheckElementChangeExt(int x, int y,
11138                                      int element,
11139                                      int trigger_element,
11140                                      int trigger_event,
11141                                      int trigger_player,
11142                                      int trigger_side)
11143 {
11144   boolean change_done = FALSE;
11145   int p;
11146
11147   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11148       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11149     return FALSE;
11150
11151   if (Feld[x][y] == EL_BLOCKED)
11152   {
11153     Blocked2Moving(x, y, &x, &y);
11154     element = Feld[x][y];
11155   }
11156
11157 #if 0
11158   /* check if element has already changed */
11159   if (Feld[x][y] != element)
11160     return FALSE;
11161 #else
11162   /* check if element has already changed or is about to change after moving */
11163   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11164        Feld[x][y] != element) ||
11165
11166       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11167        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11168         ChangePage[x][y] != -1)))
11169     return FALSE;
11170 #endif
11171
11172 #if 0
11173   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11174          trigger_event, recursion_loop_depth, recursion_loop_detected,
11175          recursion_loop_element, EL_NAME(recursion_loop_element));
11176 #endif
11177
11178   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11179
11180   for (p = 0; p < element_info[element].num_change_pages; p++)
11181   {
11182     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11183
11184     /* check trigger element for all events where the element that is checked
11185        for changing interacts with a directly adjacent element -- this is
11186        different to element changes that affect other elements to change on the
11187        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11188     boolean check_trigger_element =
11189       (trigger_event == CE_TOUCHING_X ||
11190        trigger_event == CE_HITTING_X ||
11191        trigger_event == CE_HIT_BY_X ||
11192 #if 1
11193        /* this one was forgotten until 3.2.3 */
11194        trigger_event == CE_DIGGING_X);
11195 #endif
11196
11197     if (change->can_change_or_has_action &&
11198         change->has_event[trigger_event] &&
11199         change->trigger_side & trigger_side &&
11200         change->trigger_player & trigger_player &&
11201         (!check_trigger_element ||
11202          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11203     {
11204       change->actual_trigger_element = trigger_element;
11205       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11206       change->actual_trigger_player_bits = trigger_player;
11207       change->actual_trigger_side = trigger_side;
11208       change->actual_trigger_ce_value = CustomValue[x][y];
11209       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11210
11211       /* special case: trigger element not at (x,y) position for some events */
11212       if (check_trigger_element)
11213       {
11214         static struct
11215         {
11216           int dx, dy;
11217         } move_xy[] =
11218           {
11219             {  0,  0 },
11220             { -1,  0 },
11221             { +1,  0 },
11222             {  0,  0 },
11223             {  0, -1 },
11224             {  0,  0 }, { 0, 0 }, { 0, 0 },
11225             {  0, +1 }
11226           };
11227
11228         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11229         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11230
11231         change->actual_trigger_ce_value = CustomValue[xx][yy];
11232         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11233       }
11234
11235       if (change->can_change && !change_done)
11236       {
11237         ChangeDelay[x][y] = 1;
11238         ChangeEvent[x][y] = trigger_event;
11239
11240         HandleElementChange(x, y, p);
11241
11242         change_done = TRUE;
11243       }
11244 #if USE_NEW_DELAYED_ACTION
11245       else if (change->has_action)
11246       {
11247         ExecuteCustomElementAction(x, y, element, p);
11248         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11249       }
11250 #else
11251       if (change->has_action)
11252       {
11253         ExecuteCustomElementAction(x, y, element, p);
11254         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11255       }
11256 #endif
11257     }
11258   }
11259
11260   RECURSION_LOOP_DETECTION_END();
11261
11262   return change_done;
11263 }
11264
11265 static void PlayPlayerSound(struct PlayerInfo *player)
11266 {
11267   int jx = player->jx, jy = player->jy;
11268   int sound_element = player->artwork_element;
11269   int last_action = player->last_action_waiting;
11270   int action = player->action_waiting;
11271
11272   if (player->is_waiting)
11273   {
11274     if (action != last_action)
11275       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11276     else
11277       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11278   }
11279   else
11280   {
11281     if (action != last_action)
11282       StopSound(element_info[sound_element].sound[last_action]);
11283
11284     if (last_action == ACTION_SLEEPING)
11285       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11286   }
11287 }
11288
11289 static void PlayAllPlayersSound()
11290 {
11291   int i;
11292
11293   for (i = 0; i < MAX_PLAYERS; i++)
11294     if (stored_player[i].active)
11295       PlayPlayerSound(&stored_player[i]);
11296 }
11297
11298 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11299 {
11300   boolean last_waiting = player->is_waiting;
11301   int move_dir = player->MovDir;
11302
11303   player->dir_waiting = move_dir;
11304   player->last_action_waiting = player->action_waiting;
11305
11306   if (is_waiting)
11307   {
11308     if (!last_waiting)          /* not waiting -> waiting */
11309     {
11310       player->is_waiting = TRUE;
11311
11312       player->frame_counter_bored =
11313         FrameCounter +
11314         game.player_boring_delay_fixed +
11315         GetSimpleRandom(game.player_boring_delay_random);
11316       player->frame_counter_sleeping =
11317         FrameCounter +
11318         game.player_sleeping_delay_fixed +
11319         GetSimpleRandom(game.player_sleeping_delay_random);
11320
11321       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11322     }
11323
11324     if (game.player_sleeping_delay_fixed +
11325         game.player_sleeping_delay_random > 0 &&
11326         player->anim_delay_counter == 0 &&
11327         player->post_delay_counter == 0 &&
11328         FrameCounter >= player->frame_counter_sleeping)
11329       player->is_sleeping = TRUE;
11330     else if (game.player_boring_delay_fixed +
11331              game.player_boring_delay_random > 0 &&
11332              FrameCounter >= player->frame_counter_bored)
11333       player->is_bored = TRUE;
11334
11335     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11336                               player->is_bored ? ACTION_BORING :
11337                               ACTION_WAITING);
11338
11339     if (player->is_sleeping && player->use_murphy)
11340     {
11341       /* special case for sleeping Murphy when leaning against non-free tile */
11342
11343       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11344           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11345            !IS_MOVING(player->jx - 1, player->jy)))
11346         move_dir = MV_LEFT;
11347       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11348                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11349                 !IS_MOVING(player->jx + 1, player->jy)))
11350         move_dir = MV_RIGHT;
11351       else
11352         player->is_sleeping = FALSE;
11353
11354       player->dir_waiting = move_dir;
11355     }
11356
11357     if (player->is_sleeping)
11358     {
11359       if (player->num_special_action_sleeping > 0)
11360       {
11361         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11362         {
11363           int last_special_action = player->special_action_sleeping;
11364           int num_special_action = player->num_special_action_sleeping;
11365           int special_action =
11366             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11367              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11368              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11369              last_special_action + 1 : ACTION_SLEEPING);
11370           int special_graphic =
11371             el_act_dir2img(player->artwork_element, special_action, move_dir);
11372
11373           player->anim_delay_counter =
11374             graphic_info[special_graphic].anim_delay_fixed +
11375             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11376           player->post_delay_counter =
11377             graphic_info[special_graphic].post_delay_fixed +
11378             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11379
11380           player->special_action_sleeping = special_action;
11381         }
11382
11383         if (player->anim_delay_counter > 0)
11384         {
11385           player->action_waiting = player->special_action_sleeping;
11386           player->anim_delay_counter--;
11387         }
11388         else if (player->post_delay_counter > 0)
11389         {
11390           player->post_delay_counter--;
11391         }
11392       }
11393     }
11394     else if (player->is_bored)
11395     {
11396       if (player->num_special_action_bored > 0)
11397       {
11398         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11399         {
11400           int special_action =
11401             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11402           int special_graphic =
11403             el_act_dir2img(player->artwork_element, special_action, move_dir);
11404
11405           player->anim_delay_counter =
11406             graphic_info[special_graphic].anim_delay_fixed +
11407             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11408           player->post_delay_counter =
11409             graphic_info[special_graphic].post_delay_fixed +
11410             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11411
11412           player->special_action_bored = special_action;
11413         }
11414
11415         if (player->anim_delay_counter > 0)
11416         {
11417           player->action_waiting = player->special_action_bored;
11418           player->anim_delay_counter--;
11419         }
11420         else if (player->post_delay_counter > 0)
11421         {
11422           player->post_delay_counter--;
11423         }
11424       }
11425     }
11426   }
11427   else if (last_waiting)        /* waiting -> not waiting */
11428   {
11429     player->is_waiting = FALSE;
11430     player->is_bored = FALSE;
11431     player->is_sleeping = FALSE;
11432
11433     player->frame_counter_bored = -1;
11434     player->frame_counter_sleeping = -1;
11435
11436     player->anim_delay_counter = 0;
11437     player->post_delay_counter = 0;
11438
11439     player->dir_waiting = player->MovDir;
11440     player->action_waiting = ACTION_DEFAULT;
11441
11442     player->special_action_bored = ACTION_DEFAULT;
11443     player->special_action_sleeping = ACTION_DEFAULT;
11444   }
11445 }
11446
11447 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11448 {
11449   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11450   int left      = player_action & JOY_LEFT;
11451   int right     = player_action & JOY_RIGHT;
11452   int up        = player_action & JOY_UP;
11453   int down      = player_action & JOY_DOWN;
11454   int button1   = player_action & JOY_BUTTON_1;
11455   int button2   = player_action & JOY_BUTTON_2;
11456   int dx        = (left ? -1 : right ? 1 : 0);
11457   int dy        = (up   ? -1 : down  ? 1 : 0);
11458
11459   if (!player->active || tape.pausing)
11460     return 0;
11461
11462   if (player_action)
11463   {
11464     if (button1)
11465       snapped = SnapField(player, dx, dy);
11466     else
11467     {
11468       if (button2)
11469         dropped = DropElement(player);
11470
11471       moved = MovePlayer(player, dx, dy);
11472     }
11473
11474     if (tape.single_step && tape.recording && !tape.pausing)
11475     {
11476       if (button1 || (dropped && !moved))
11477       {
11478         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11479         SnapField(player, 0, 0);                /* stop snapping */
11480       }
11481     }
11482
11483     SetPlayerWaiting(player, FALSE);
11484
11485     return player_action;
11486   }
11487   else
11488   {
11489     /* no actions for this player (no input at player's configured device) */
11490
11491     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11492     SnapField(player, 0, 0);
11493     CheckGravityMovementWhenNotMoving(player);
11494
11495     if (player->MovPos == 0)
11496       SetPlayerWaiting(player, TRUE);
11497
11498     if (player->MovPos == 0)    /* needed for tape.playing */
11499       player->is_moving = FALSE;
11500
11501     player->is_dropping = FALSE;
11502     player->is_dropping_pressed = FALSE;
11503     player->drop_pressed_delay = 0;
11504
11505     return 0;
11506   }
11507 }
11508
11509 static void CheckLevelTime()
11510 {
11511   int i;
11512
11513   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11514   {
11515     if (level.native_em_level->lev->home == 0)  /* all players at home */
11516     {
11517       PlayerWins(local_player);
11518
11519       AllPlayersGone = TRUE;
11520
11521       level.native_em_level->lev->home = -1;
11522     }
11523
11524     if (level.native_em_level->ply[0]->alive == 0 &&
11525         level.native_em_level->ply[1]->alive == 0 &&
11526         level.native_em_level->ply[2]->alive == 0 &&
11527         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11528       AllPlayersGone = TRUE;
11529   }
11530
11531   if (TimeFrames >= FRAMES_PER_SECOND)
11532   {
11533     TimeFrames = 0;
11534     TapeTime++;
11535
11536     for (i = 0; i < MAX_PLAYERS; i++)
11537     {
11538       struct PlayerInfo *player = &stored_player[i];
11539
11540       if (SHIELD_ON(player))
11541       {
11542         player->shield_normal_time_left--;
11543
11544         if (player->shield_deadly_time_left > 0)
11545           player->shield_deadly_time_left--;
11546       }
11547     }
11548
11549     if (!local_player->LevelSolved && !level.use_step_counter)
11550     {
11551       TimePlayed++;
11552
11553       if (TimeLeft > 0)
11554       {
11555         TimeLeft--;
11556
11557         if (TimeLeft <= 10 && setup.time_limit)
11558           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11559
11560 #if 1
11561         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11562
11563         DisplayGameControlValues();
11564 #else
11565         DrawGameValue_Time(TimeLeft);
11566 #endif
11567
11568         if (!TimeLeft && setup.time_limit)
11569         {
11570           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11571             level.native_em_level->lev->killed_out_of_time = TRUE;
11572           else
11573             for (i = 0; i < MAX_PLAYERS; i++)
11574               KillPlayer(&stored_player[i]);
11575         }
11576       }
11577 #if 1
11578       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11579       {
11580         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11581
11582         DisplayGameControlValues();
11583       }
11584 #else
11585       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11586         DrawGameValue_Time(TimePlayed);
11587 #endif
11588
11589       level.native_em_level->lev->time =
11590         (level.time == 0 ? TimePlayed : TimeLeft);
11591     }
11592
11593     if (tape.recording || tape.playing)
11594       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11595   }
11596
11597 #if 1
11598   UpdateAndDisplayGameControlValues();
11599 #else
11600   UpdateGameDoorValues();
11601   DrawGameDoorValues();
11602 #endif
11603 }
11604
11605 void AdvanceFrameAndPlayerCounters(int player_nr)
11606 {
11607   int i;
11608
11609   /* advance frame counters (global frame counter and time frame counter) */
11610   FrameCounter++;
11611   TimeFrames++;
11612
11613   /* advance player counters (counters for move delay, move animation etc.) */
11614   for (i = 0; i < MAX_PLAYERS; i++)
11615   {
11616     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11617     int move_delay_value = stored_player[i].move_delay_value;
11618     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11619
11620     if (!advance_player_counters)       /* not all players may be affected */
11621       continue;
11622
11623 #if USE_NEW_PLAYER_ANIM
11624     if (move_frames == 0)       /* less than one move per game frame */
11625     {
11626       int stepsize = TILEX / move_delay_value;
11627       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11628       int count = (stored_player[i].is_moving ?
11629                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11630
11631       if (count % delay == 0)
11632         move_frames = 1;
11633     }
11634 #endif
11635
11636     stored_player[i].Frame += move_frames;
11637
11638     if (stored_player[i].MovPos != 0)
11639       stored_player[i].StepFrame += move_frames;
11640
11641     if (stored_player[i].move_delay > 0)
11642       stored_player[i].move_delay--;
11643
11644     /* due to bugs in previous versions, counter must count up, not down */
11645     if (stored_player[i].push_delay != -1)
11646       stored_player[i].push_delay++;
11647
11648     if (stored_player[i].drop_delay > 0)
11649       stored_player[i].drop_delay--;
11650
11651     if (stored_player[i].is_dropping_pressed)
11652       stored_player[i].drop_pressed_delay++;
11653   }
11654 }
11655
11656 void StartGameActions(boolean init_network_game, boolean record_tape,
11657                       long random_seed)
11658 {
11659   unsigned long new_random_seed = InitRND(random_seed);
11660
11661   if (record_tape)
11662     TapeStartRecording(new_random_seed);
11663
11664 #if defined(NETWORK_AVALIABLE)
11665   if (init_network_game)
11666   {
11667     SendToServer_StartPlaying();
11668
11669     return;
11670   }
11671 #endif
11672
11673   InitGame();
11674 }
11675
11676 void GameActions()
11677 {
11678   static unsigned long game_frame_delay = 0;
11679   unsigned long game_frame_delay_value;
11680   byte *recorded_player_action;
11681   byte summarized_player_action = 0;
11682   byte tape_action[MAX_PLAYERS];
11683   int i;
11684
11685   /* detect endless loops, caused by custom element programming */
11686   if (recursion_loop_detected && recursion_loop_depth == 0)
11687   {
11688     char *message = getStringCat3("Internal Error ! Element ",
11689                                   EL_NAME(recursion_loop_element),
11690                                   " caused endless loop ! Quit the game ?");
11691
11692     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11693           EL_NAME(recursion_loop_element));
11694
11695     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11696
11697     recursion_loop_detected = FALSE;    /* if game should be continued */
11698
11699     free(message);
11700
11701     return;
11702   }
11703
11704   if (game.restart_level)
11705     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11706
11707   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11708   {
11709     if (level.native_em_level->lev->home == 0)  /* all players at home */
11710     {
11711       PlayerWins(local_player);
11712
11713       AllPlayersGone = TRUE;
11714
11715       level.native_em_level->lev->home = -1;
11716     }
11717
11718     if (level.native_em_level->ply[0]->alive == 0 &&
11719         level.native_em_level->ply[1]->alive == 0 &&
11720         level.native_em_level->ply[2]->alive == 0 &&
11721         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11722       AllPlayersGone = TRUE;
11723   }
11724
11725   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11726     GameWon();
11727
11728   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11729     TapeStop();
11730
11731   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11732     return;
11733
11734   game_frame_delay_value =
11735     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11736
11737   if (tape.playing && tape.warp_forward && !tape.pausing)
11738     game_frame_delay_value = 0;
11739
11740   /* ---------- main game synchronization point ---------- */
11741
11742   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11743
11744   if (network_playing && !network_player_action_received)
11745   {
11746     /* try to get network player actions in time */
11747
11748 #if defined(NETWORK_AVALIABLE)
11749     /* last chance to get network player actions without main loop delay */
11750     HandleNetworking();
11751 #endif
11752
11753     /* game was quit by network peer */
11754     if (game_status != GAME_MODE_PLAYING)
11755       return;
11756
11757     if (!network_player_action_received)
11758       return;           /* failed to get network player actions in time */
11759
11760     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11761   }
11762
11763   if (tape.pausing)
11764     return;
11765
11766   /* at this point we know that we really continue executing the game */
11767
11768   network_player_action_received = FALSE;
11769
11770   /* when playing tape, read previously recorded player input from tape data */
11771   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11772
11773 #if 1
11774   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11775   if (tape.pausing)
11776     return;
11777 #endif
11778
11779   if (tape.set_centered_player)
11780   {
11781     game.centered_player_nr_next = tape.centered_player_nr_next;
11782     game.set_centered_player = TRUE;
11783   }
11784
11785   for (i = 0; i < MAX_PLAYERS; i++)
11786   {
11787     summarized_player_action |= stored_player[i].action;
11788
11789     if (!network_playing)
11790       stored_player[i].effective_action = stored_player[i].action;
11791   }
11792
11793 #if defined(NETWORK_AVALIABLE)
11794   if (network_playing)
11795     SendToServer_MovePlayer(summarized_player_action);
11796 #endif
11797
11798   if (!options.network && !setup.team_mode)
11799     local_player->effective_action = summarized_player_action;
11800
11801   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11802   {
11803     for (i = 0; i < MAX_PLAYERS; i++)
11804       stored_player[i].effective_action =
11805         (i == game.centered_player_nr ? summarized_player_action : 0);
11806   }
11807
11808   if (recorded_player_action != NULL)
11809     for (i = 0; i < MAX_PLAYERS; i++)
11810       stored_player[i].effective_action = recorded_player_action[i];
11811
11812   for (i = 0; i < MAX_PLAYERS; i++)
11813   {
11814     tape_action[i] = stored_player[i].effective_action;
11815
11816     /* (this can only happen in the R'n'D game engine) */
11817     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11818       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11819   }
11820
11821   /* only record actions from input devices, but not programmed actions */
11822   if (tape.recording)
11823     TapeRecordAction(tape_action);
11824
11825   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11826   {
11827     GameActions_EM_Main();
11828   }
11829   else
11830   {
11831     GameActions_RND();
11832   }
11833 }
11834
11835 void GameActions_EM_Main()
11836 {
11837   byte effective_action[MAX_PLAYERS];
11838   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11839   int i;
11840
11841   for (i = 0; i < MAX_PLAYERS; i++)
11842     effective_action[i] = stored_player[i].effective_action;
11843
11844   GameActions_EM(effective_action, warp_mode);
11845
11846   CheckLevelTime();
11847
11848   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11849 }
11850
11851 void GameActions_RND()
11852 {
11853   int magic_wall_x = 0, magic_wall_y = 0;
11854   int i, x, y, element, graphic;
11855
11856   InitPlayfieldScanModeVars();
11857
11858 #if USE_ONE_MORE_CHANGE_PER_FRAME
11859   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11860   {
11861     SCAN_PLAYFIELD(x, y)
11862     {
11863       ChangeCount[x][y] = 0;
11864       ChangeEvent[x][y] = -1;
11865     }
11866   }
11867 #endif
11868
11869   if (game.set_centered_player)
11870   {
11871     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11872
11873     /* switching to "all players" only possible if all players fit to screen */
11874     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11875     {
11876       game.centered_player_nr_next = game.centered_player_nr;
11877       game.set_centered_player = FALSE;
11878     }
11879
11880     /* do not switch focus to non-existing (or non-active) player */
11881     if (game.centered_player_nr_next >= 0 &&
11882         !stored_player[game.centered_player_nr_next].active)
11883     {
11884       game.centered_player_nr_next = game.centered_player_nr;
11885       game.set_centered_player = FALSE;
11886     }
11887   }
11888
11889   if (game.set_centered_player &&
11890       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11891   {
11892     int sx, sy;
11893
11894     if (game.centered_player_nr_next == -1)
11895     {
11896       setScreenCenteredToAllPlayers(&sx, &sy);
11897     }
11898     else
11899     {
11900       sx = stored_player[game.centered_player_nr_next].jx;
11901       sy = stored_player[game.centered_player_nr_next].jy;
11902     }
11903
11904     game.centered_player_nr = game.centered_player_nr_next;
11905     game.set_centered_player = FALSE;
11906
11907     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11908     DrawGameDoorValues();
11909   }
11910
11911   for (i = 0; i < MAX_PLAYERS; i++)
11912   {
11913     int actual_player_action = stored_player[i].effective_action;
11914
11915 #if 1
11916     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11917        - rnd_equinox_tetrachloride 048
11918        - rnd_equinox_tetrachloride_ii 096
11919        - rnd_emanuel_schmieg 002
11920        - doctor_sloan_ww 001, 020
11921     */
11922     if (stored_player[i].MovPos == 0)
11923       CheckGravityMovement(&stored_player[i]);
11924 #endif
11925
11926     /* overwrite programmed action with tape action */
11927     if (stored_player[i].programmed_action)
11928       actual_player_action = stored_player[i].programmed_action;
11929
11930     PlayerActions(&stored_player[i], actual_player_action);
11931
11932     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11933   }
11934
11935   ScrollScreen(NULL, SCROLL_GO_ON);
11936
11937   /* for backwards compatibility, the following code emulates a fixed bug that
11938      occured when pushing elements (causing elements that just made their last
11939      pushing step to already (if possible) make their first falling step in the
11940      same game frame, which is bad); this code is also needed to use the famous
11941      "spring push bug" which is used in older levels and might be wanted to be
11942      used also in newer levels, but in this case the buggy pushing code is only
11943      affecting the "spring" element and no other elements */
11944
11945   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11946   {
11947     for (i = 0; i < MAX_PLAYERS; i++)
11948     {
11949       struct PlayerInfo *player = &stored_player[i];
11950       int x = player->jx;
11951       int y = player->jy;
11952
11953       if (player->active && player->is_pushing && player->is_moving &&
11954           IS_MOVING(x, y) &&
11955           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11956            Feld[x][y] == EL_SPRING))
11957       {
11958         ContinueMoving(x, y);
11959
11960         /* continue moving after pushing (this is actually a bug) */
11961         if (!IS_MOVING(x, y))
11962           Stop[x][y] = FALSE;
11963       }
11964     }
11965   }
11966
11967 #if 0
11968   debug_print_timestamp(0, "start main loop profiling");
11969 #endif
11970
11971   SCAN_PLAYFIELD(x, y)
11972   {
11973     ChangeCount[x][y] = 0;
11974     ChangeEvent[x][y] = -1;
11975
11976     /* this must be handled before main playfield loop */
11977     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11978     {
11979       MovDelay[x][y]--;
11980       if (MovDelay[x][y] <= 0)
11981         RemoveField(x, y);
11982     }
11983
11984 #if USE_NEW_SNAP_DELAY
11985     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11986     {
11987       MovDelay[x][y]--;
11988       if (MovDelay[x][y] <= 0)
11989       {
11990         RemoveField(x, y);
11991         TEST_DrawLevelField(x, y);
11992
11993         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11994       }
11995     }
11996 #endif
11997
11998 #if DEBUG
11999     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12000     {
12001       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12002       printf("GameActions(): This should never happen!\n");
12003
12004       ChangePage[x][y] = -1;
12005     }
12006 #endif
12007
12008     Stop[x][y] = FALSE;
12009     if (WasJustMoving[x][y] > 0)
12010       WasJustMoving[x][y]--;
12011     if (WasJustFalling[x][y] > 0)
12012       WasJustFalling[x][y]--;
12013     if (CheckCollision[x][y] > 0)
12014       CheckCollision[x][y]--;
12015     if (CheckImpact[x][y] > 0)
12016       CheckImpact[x][y]--;
12017
12018     GfxFrame[x][y]++;
12019
12020     /* reset finished pushing action (not done in ContinueMoving() to allow
12021        continuous pushing animation for elements with zero push delay) */
12022     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12023     {
12024       ResetGfxAnimation(x, y);
12025       TEST_DrawLevelField(x, y);
12026     }
12027
12028 #if DEBUG
12029     if (IS_BLOCKED(x, y))
12030     {
12031       int oldx, oldy;
12032
12033       Blocked2Moving(x, y, &oldx, &oldy);
12034       if (!IS_MOVING(oldx, oldy))
12035       {
12036         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12037         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12038         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12039         printf("GameActions(): This should never happen!\n");
12040       }
12041     }
12042 #endif
12043   }
12044
12045 #if 0
12046   debug_print_timestamp(0, "- time for pre-main loop:");
12047 #endif
12048
12049 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12050   SCAN_PLAYFIELD(x, y)
12051   {
12052     element = Feld[x][y];
12053     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12054
12055 #if 1
12056     {
12057 #if 1
12058       int element2 = element;
12059       int graphic2 = graphic;
12060 #else
12061       int element2 = Feld[x][y];
12062       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12063 #endif
12064       int last_gfx_frame = GfxFrame[x][y];
12065
12066       if (graphic_info[graphic2].anim_global_sync)
12067         GfxFrame[x][y] = FrameCounter;
12068       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12069         GfxFrame[x][y] = CustomValue[x][y];
12070       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12071         GfxFrame[x][y] = element_info[element2].collect_score;
12072       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12073         GfxFrame[x][y] = ChangeDelay[x][y];
12074
12075       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12076         DrawLevelGraphicAnimation(x, y, graphic2);
12077     }
12078 #else
12079     ResetGfxFrame(x, y, TRUE);
12080 #endif
12081
12082 #if 1
12083     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12084         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12085       ResetRandomAnimationValue(x, y);
12086 #endif
12087
12088 #if 1
12089     SetRandomAnimationValue(x, y);
12090 #endif
12091
12092 #if 1
12093     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12094 #endif
12095   }
12096 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12097
12098 #if 0
12099   debug_print_timestamp(0, "- time for TEST loop:     -->");
12100 #endif
12101
12102   SCAN_PLAYFIELD(x, y)
12103   {
12104     element = Feld[x][y];
12105     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12106
12107     ResetGfxFrame(x, y, TRUE);
12108
12109     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12110         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12111       ResetRandomAnimationValue(x, y);
12112
12113     SetRandomAnimationValue(x, y);
12114
12115     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12116
12117     if (IS_INACTIVE(element))
12118     {
12119       if (IS_ANIMATED(graphic))
12120         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12121
12122       continue;
12123     }
12124
12125     /* this may take place after moving, so 'element' may have changed */
12126     if (IS_CHANGING(x, y) &&
12127         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12128     {
12129       int page = element_info[element].event_page_nr[CE_DELAY];
12130
12131 #if 1
12132       HandleElementChange(x, y, page);
12133 #else
12134       if (CAN_CHANGE(element))
12135         HandleElementChange(x, y, page);
12136
12137       if (HAS_ACTION(element))
12138         ExecuteCustomElementAction(x, y, element, page);
12139 #endif
12140
12141       element = Feld[x][y];
12142       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12143     }
12144
12145 #if 0   // ---------------------------------------------------------------------
12146
12147     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12148     {
12149       StartMoving(x, y);
12150
12151       element = Feld[x][y];
12152       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12153
12154       if (IS_ANIMATED(graphic) &&
12155           !IS_MOVING(x, y) &&
12156           !Stop[x][y])
12157         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12158
12159       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12160         TEST_DrawTwinkleOnField(x, y);
12161     }
12162     else if (IS_MOVING(x, y))
12163       ContinueMoving(x, y);
12164     else
12165     {
12166       switch (element)
12167       {
12168         case EL_ACID:
12169         case EL_EXIT_OPEN:
12170         case EL_EM_EXIT_OPEN:
12171         case EL_SP_EXIT_OPEN:
12172         case EL_STEEL_EXIT_OPEN:
12173         case EL_EM_STEEL_EXIT_OPEN:
12174         case EL_SP_TERMINAL:
12175         case EL_SP_TERMINAL_ACTIVE:
12176         case EL_EXTRA_TIME:
12177         case EL_SHIELD_NORMAL:
12178         case EL_SHIELD_DEADLY:
12179           if (IS_ANIMATED(graphic))
12180             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12181           break;
12182
12183         case EL_DYNAMITE_ACTIVE:
12184         case EL_EM_DYNAMITE_ACTIVE:
12185         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12186         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12187         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12188         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12189         case EL_SP_DISK_RED_ACTIVE:
12190           CheckDynamite(x, y);
12191           break;
12192
12193         case EL_AMOEBA_GROWING:
12194           AmoebeWaechst(x, y);
12195           break;
12196
12197         case EL_AMOEBA_SHRINKING:
12198           AmoebaDisappearing(x, y);
12199           break;
12200
12201 #if !USE_NEW_AMOEBA_CODE
12202         case EL_AMOEBA_WET:
12203         case EL_AMOEBA_DRY:
12204         case EL_AMOEBA_FULL:
12205         case EL_BD_AMOEBA:
12206         case EL_EMC_DRIPPER:
12207           AmoebeAbleger(x, y);
12208           break;
12209 #endif
12210
12211         case EL_GAME_OF_LIFE:
12212         case EL_BIOMAZE:
12213           Life(x, y);
12214           break;
12215
12216         case EL_EXIT_CLOSED:
12217           CheckExit(x, y);
12218           break;
12219
12220         case EL_EM_EXIT_CLOSED:
12221           CheckExitEM(x, y);
12222           break;
12223
12224         case EL_STEEL_EXIT_CLOSED:
12225           CheckExitSteel(x, y);
12226           break;
12227
12228         case EL_EM_STEEL_EXIT_CLOSED:
12229           CheckExitSteelEM(x, y);
12230           break;
12231
12232         case EL_SP_EXIT_CLOSED:
12233           CheckExitSP(x, y);
12234           break;
12235
12236         case EL_EXPANDABLE_WALL_GROWING:
12237         case EL_EXPANDABLE_STEELWALL_GROWING:
12238           MauerWaechst(x, y);
12239           break;
12240
12241         case EL_EXPANDABLE_WALL:
12242         case EL_EXPANDABLE_WALL_HORIZONTAL:
12243         case EL_EXPANDABLE_WALL_VERTICAL:
12244         case EL_EXPANDABLE_WALL_ANY:
12245         case EL_BD_EXPANDABLE_WALL:
12246           MauerAbleger(x, y);
12247           break;
12248
12249         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12250         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12251         case EL_EXPANDABLE_STEELWALL_ANY:
12252           MauerAblegerStahl(x, y);
12253           break;
12254
12255         case EL_FLAMES:
12256           CheckForDragon(x, y);
12257           break;
12258
12259         case EL_EXPLOSION:
12260           break;
12261
12262         case EL_ELEMENT_SNAPPING:
12263         case EL_DIAGONAL_SHRINKING:
12264         case EL_DIAGONAL_GROWING:
12265         {
12266           graphic =
12267             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12268
12269           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12270           break;
12271         }
12272
12273         default:
12274           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12275             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12276           break;
12277       }
12278     }
12279
12280 #else   // ---------------------------------------------------------------------
12281
12282     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12283     {
12284       StartMoving(x, y);
12285
12286       element = Feld[x][y];
12287       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12288
12289       if (IS_ANIMATED(graphic) &&
12290           !IS_MOVING(x, y) &&
12291           !Stop[x][y])
12292         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12293
12294       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12295         TEST_DrawTwinkleOnField(x, y);
12296     }
12297     else if ((element == EL_ACID ||
12298               element == EL_EXIT_OPEN ||
12299               element == EL_EM_EXIT_OPEN ||
12300               element == EL_SP_EXIT_OPEN ||
12301               element == EL_STEEL_EXIT_OPEN ||
12302               element == EL_EM_STEEL_EXIT_OPEN ||
12303               element == EL_SP_TERMINAL ||
12304               element == EL_SP_TERMINAL_ACTIVE ||
12305               element == EL_EXTRA_TIME ||
12306               element == EL_SHIELD_NORMAL ||
12307               element == EL_SHIELD_DEADLY) &&
12308              IS_ANIMATED(graphic))
12309       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12310     else if (IS_MOVING(x, y))
12311       ContinueMoving(x, y);
12312     else if (IS_ACTIVE_BOMB(element))
12313       CheckDynamite(x, y);
12314     else if (element == EL_AMOEBA_GROWING)
12315       AmoebeWaechst(x, y);
12316     else if (element == EL_AMOEBA_SHRINKING)
12317       AmoebaDisappearing(x, y);
12318
12319 #if !USE_NEW_AMOEBA_CODE
12320     else if (IS_AMOEBALIVE(element))
12321       AmoebeAbleger(x, y);
12322 #endif
12323
12324     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12325       Life(x, y);
12326     else if (element == EL_EXIT_CLOSED)
12327       CheckExit(x, y);
12328     else if (element == EL_EM_EXIT_CLOSED)
12329       CheckExitEM(x, y);
12330     else if (element == EL_STEEL_EXIT_CLOSED)
12331       CheckExitSteel(x, y);
12332     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12333       CheckExitSteelEM(x, y);
12334     else if (element == EL_SP_EXIT_CLOSED)
12335       CheckExitSP(x, y);
12336     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12337              element == EL_EXPANDABLE_STEELWALL_GROWING)
12338       MauerWaechst(x, y);
12339     else if (element == EL_EXPANDABLE_WALL ||
12340              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12341              element == EL_EXPANDABLE_WALL_VERTICAL ||
12342              element == EL_EXPANDABLE_WALL_ANY ||
12343              element == EL_BD_EXPANDABLE_WALL)
12344       MauerAbleger(x, y);
12345     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12346              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12347              element == EL_EXPANDABLE_STEELWALL_ANY)
12348       MauerAblegerStahl(x, y);
12349     else if (element == EL_FLAMES)
12350       CheckForDragon(x, y);
12351     else if (element == EL_EXPLOSION)
12352       ; /* drawing of correct explosion animation is handled separately */
12353     else if (element == EL_ELEMENT_SNAPPING ||
12354              element == EL_DIAGONAL_SHRINKING ||
12355              element == EL_DIAGONAL_GROWING)
12356     {
12357       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12358
12359       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12360     }
12361     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12362       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12363
12364 #endif  // ---------------------------------------------------------------------
12365
12366     if (IS_BELT_ACTIVE(element))
12367       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12368
12369     if (game.magic_wall_active)
12370     {
12371       int jx = local_player->jx, jy = local_player->jy;
12372
12373       /* play the element sound at the position nearest to the player */
12374       if ((element == EL_MAGIC_WALL_FULL ||
12375            element == EL_MAGIC_WALL_ACTIVE ||
12376            element == EL_MAGIC_WALL_EMPTYING ||
12377            element == EL_BD_MAGIC_WALL_FULL ||
12378            element == EL_BD_MAGIC_WALL_ACTIVE ||
12379            element == EL_BD_MAGIC_WALL_EMPTYING ||
12380            element == EL_DC_MAGIC_WALL_FULL ||
12381            element == EL_DC_MAGIC_WALL_ACTIVE ||
12382            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12383           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12384       {
12385         magic_wall_x = x;
12386         magic_wall_y = y;
12387       }
12388     }
12389   }
12390
12391 #if 0
12392   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12393 #endif
12394
12395 #if USE_NEW_AMOEBA_CODE
12396   /* new experimental amoeba growth stuff */
12397   if (!(FrameCounter % 8))
12398   {
12399     static unsigned long random = 1684108901;
12400
12401     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12402     {
12403       x = RND(lev_fieldx);
12404       y = RND(lev_fieldy);
12405       element = Feld[x][y];
12406
12407       if (!IS_PLAYER(x,y) &&
12408           (element == EL_EMPTY ||
12409            CAN_GROW_INTO(element) ||
12410            element == EL_QUICKSAND_EMPTY ||
12411            element == EL_QUICKSAND_FAST_EMPTY ||
12412            element == EL_ACID_SPLASH_LEFT ||
12413            element == EL_ACID_SPLASH_RIGHT))
12414       {
12415         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12416             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12417             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12418             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12419           Feld[x][y] = EL_AMOEBA_DROP;
12420       }
12421
12422       random = random * 129 + 1;
12423     }
12424   }
12425 #endif
12426
12427 #if 0
12428   if (game.explosions_delayed)
12429 #endif
12430   {
12431     game.explosions_delayed = FALSE;
12432
12433     SCAN_PLAYFIELD(x, y)
12434     {
12435       element = Feld[x][y];
12436
12437       if (ExplodeField[x][y])
12438         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12439       else if (element == EL_EXPLOSION)
12440         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12441
12442       ExplodeField[x][y] = EX_TYPE_NONE;
12443     }
12444
12445     game.explosions_delayed = TRUE;
12446   }
12447
12448   if (game.magic_wall_active)
12449   {
12450     if (!(game.magic_wall_time_left % 4))
12451     {
12452       int element = Feld[magic_wall_x][magic_wall_y];
12453
12454       if (element == EL_BD_MAGIC_WALL_FULL ||
12455           element == EL_BD_MAGIC_WALL_ACTIVE ||
12456           element == EL_BD_MAGIC_WALL_EMPTYING)
12457         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12458       else if (element == EL_DC_MAGIC_WALL_FULL ||
12459                element == EL_DC_MAGIC_WALL_ACTIVE ||
12460                element == EL_DC_MAGIC_WALL_EMPTYING)
12461         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12462       else
12463         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12464     }
12465
12466     if (game.magic_wall_time_left > 0)
12467     {
12468       game.magic_wall_time_left--;
12469
12470       if (!game.magic_wall_time_left)
12471       {
12472         SCAN_PLAYFIELD(x, y)
12473         {
12474           element = Feld[x][y];
12475
12476           if (element == EL_MAGIC_WALL_ACTIVE ||
12477               element == EL_MAGIC_WALL_FULL)
12478           {
12479             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12480             TEST_DrawLevelField(x, y);
12481           }
12482           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12483                    element == EL_BD_MAGIC_WALL_FULL)
12484           {
12485             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12486             TEST_DrawLevelField(x, y);
12487           }
12488           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12489                    element == EL_DC_MAGIC_WALL_FULL)
12490           {
12491             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12492             TEST_DrawLevelField(x, y);
12493           }
12494         }
12495
12496         game.magic_wall_active = FALSE;
12497       }
12498     }
12499   }
12500
12501   if (game.light_time_left > 0)
12502   {
12503     game.light_time_left--;
12504
12505     if (game.light_time_left == 0)
12506       RedrawAllLightSwitchesAndInvisibleElements();
12507   }
12508
12509   if (game.timegate_time_left > 0)
12510   {
12511     game.timegate_time_left--;
12512
12513     if (game.timegate_time_left == 0)
12514       CloseAllOpenTimegates();
12515   }
12516
12517   if (game.lenses_time_left > 0)
12518   {
12519     game.lenses_time_left--;
12520
12521     if (game.lenses_time_left == 0)
12522       RedrawAllInvisibleElementsForLenses();
12523   }
12524
12525   if (game.magnify_time_left > 0)
12526   {
12527     game.magnify_time_left--;
12528
12529     if (game.magnify_time_left == 0)
12530       RedrawAllInvisibleElementsForMagnifier();
12531   }
12532
12533   for (i = 0; i < MAX_PLAYERS; i++)
12534   {
12535     struct PlayerInfo *player = &stored_player[i];
12536
12537     if (SHIELD_ON(player))
12538     {
12539       if (player->shield_deadly_time_left)
12540         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12541       else if (player->shield_normal_time_left)
12542         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12543     }
12544   }
12545
12546 #if USE_DELAYED_GFX_REDRAW
12547   SCAN_PLAYFIELD(x, y)
12548   {
12549 #if 1
12550     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12551 #else
12552     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
12553         GfxRedraw[x][y] != GFX_REDRAW_NONE)
12554 #endif
12555     {
12556       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12557         DrawLevelField(x, y);
12558
12559       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12560         DrawLevelFieldCrumbledSand(x, y);
12561
12562       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12563         DrawLevelFieldCrumbledSandNeighbours(x, y);
12564
12565       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12566         DrawTwinkleOnField(x, y);
12567     }
12568
12569     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12570   }
12571 #endif
12572
12573   CheckLevelTime();
12574
12575   DrawAllPlayers();
12576   PlayAllPlayersSound();
12577
12578   if (options.debug)                    /* calculate frames per second */
12579   {
12580     static unsigned long fps_counter = 0;
12581     static int fps_frames = 0;
12582     unsigned long fps_delay_ms = Counter() - fps_counter;
12583
12584     fps_frames++;
12585
12586     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12587     {
12588       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12589
12590       fps_frames = 0;
12591       fps_counter = Counter();
12592     }
12593
12594     redraw_mask |= REDRAW_FPS;
12595   }
12596
12597   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12598
12599   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12600   {
12601     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12602
12603     local_player->show_envelope = 0;
12604   }
12605
12606 #if 0
12607   debug_print_timestamp(0, "stop main loop profiling ");
12608   printf("----------------------------------------------------------\n");
12609 #endif
12610
12611   /* use random number generator in every frame to make it less predictable */
12612   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12613     RND(1);
12614 }
12615
12616 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12617 {
12618   int min_x = x, min_y = y, max_x = x, max_y = y;
12619   int i;
12620
12621   for (i = 0; i < MAX_PLAYERS; i++)
12622   {
12623     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12624
12625     if (!stored_player[i].active || &stored_player[i] == player)
12626       continue;
12627
12628     min_x = MIN(min_x, jx);
12629     min_y = MIN(min_y, jy);
12630     max_x = MAX(max_x, jx);
12631     max_y = MAX(max_y, jy);
12632   }
12633
12634   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12635 }
12636
12637 static boolean AllPlayersInVisibleScreen()
12638 {
12639   int i;
12640
12641   for (i = 0; i < MAX_PLAYERS; i++)
12642   {
12643     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12644
12645     if (!stored_player[i].active)
12646       continue;
12647
12648     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12649       return FALSE;
12650   }
12651
12652   return TRUE;
12653 }
12654
12655 void ScrollLevel(int dx, int dy)
12656 {
12657 #if 1
12658   static Bitmap *bitmap_db_field2 = NULL;
12659   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12660   int x, y;
12661 #else
12662   int i, x, y;
12663 #endif
12664
12665 #if 0
12666   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12667   /* only horizontal XOR vertical scroll direction allowed */
12668   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12669     return;
12670 #endif
12671
12672 #if 1
12673   if (bitmap_db_field2 == NULL)
12674     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12675
12676   /* needed when blitting directly to same bitmap -- should not be needed with
12677      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12678   BlitBitmap(drawto_field, bitmap_db_field2,
12679              FX + TILEX * (dx == -1) - softscroll_offset,
12680              FY + TILEY * (dy == -1) - softscroll_offset,
12681              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12682              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12683              FX + TILEX * (dx == 1) - softscroll_offset,
12684              FY + TILEY * (dy == 1) - softscroll_offset);
12685   BlitBitmap(bitmap_db_field2, drawto_field,
12686              FX + TILEX * (dx == 1) - softscroll_offset,
12687              FY + TILEY * (dy == 1) - softscroll_offset,
12688              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12689              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12690              FX + TILEX * (dx == 1) - softscroll_offset,
12691              FY + TILEY * (dy == 1) - softscroll_offset);
12692
12693 #else
12694
12695 #if 0
12696   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12697   int xsize = (BX2 - BX1 + 1);
12698   int ysize = (BY2 - BY1 + 1);
12699   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12700   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12701   int step  = (start < end ? +1 : -1);
12702
12703   for (i = start; i != end; i += step)
12704   {
12705     BlitBitmap(drawto_field, drawto_field,
12706                FX + TILEX * (dx != 0 ? i + step : 0),
12707                FY + TILEY * (dy != 0 ? i + step : 0),
12708                TILEX * (dx != 0 ? 1 : xsize),
12709                TILEY * (dy != 0 ? 1 : ysize),
12710                FX + TILEX * (dx != 0 ? i : 0),
12711                FY + TILEY * (dy != 0 ? i : 0));
12712   }
12713
12714 #else
12715
12716   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12717
12718   BlitBitmap(drawto_field, drawto_field,
12719              FX + TILEX * (dx == -1) - softscroll_offset,
12720              FY + TILEY * (dy == -1) - softscroll_offset,
12721              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12722              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12723              FX + TILEX * (dx == 1) - softscroll_offset,
12724              FY + TILEY * (dy == 1) - softscroll_offset);
12725 #endif
12726 #endif
12727
12728   if (dx != 0)
12729   {
12730     x = (dx == 1 ? BX1 : BX2);
12731     for (y = BY1; y <= BY2; y++)
12732       DrawScreenField(x, y);
12733   }
12734
12735   if (dy != 0)
12736   {
12737     y = (dy == 1 ? BY1 : BY2);
12738     for (x = BX1; x <= BX2; x++)
12739       DrawScreenField(x, y);
12740   }
12741
12742   redraw_mask |= REDRAW_FIELD;
12743 }
12744
12745 static boolean canFallDown(struct PlayerInfo *player)
12746 {
12747   int jx = player->jx, jy = player->jy;
12748
12749   return (IN_LEV_FIELD(jx, jy + 1) &&
12750           (IS_FREE(jx, jy + 1) ||
12751            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12752           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12753           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12754 }
12755
12756 static boolean canPassField(int x, int y, int move_dir)
12757 {
12758   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12759   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12760   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12761   int nextx = x + dx;
12762   int nexty = y + dy;
12763   int element = Feld[x][y];
12764
12765   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12766           !CAN_MOVE(element) &&
12767           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12768           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12769           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12770 }
12771
12772 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12773 {
12774   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12775   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12776   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12777   int newx = x + dx;
12778   int newy = y + dy;
12779
12780   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12781           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12782           (IS_DIGGABLE(Feld[newx][newy]) ||
12783            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12784            canPassField(newx, newy, move_dir)));
12785 }
12786
12787 static void CheckGravityMovement(struct PlayerInfo *player)
12788 {
12789 #if USE_PLAYER_GRAVITY
12790   if (player->gravity && !player->programmed_action)
12791 #else
12792   if (game.gravity && !player->programmed_action)
12793 #endif
12794   {
12795     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12796     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12797     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12798     int jx = player->jx, jy = player->jy;
12799     boolean player_is_moving_to_valid_field =
12800       (!player_is_snapping &&
12801        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12802         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12803     boolean player_can_fall_down = canFallDown(player);
12804
12805     if (player_can_fall_down &&
12806         !player_is_moving_to_valid_field)
12807       player->programmed_action = MV_DOWN;
12808   }
12809 }
12810
12811 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12812 {
12813   return CheckGravityMovement(player);
12814
12815 #if USE_PLAYER_GRAVITY
12816   if (player->gravity && !player->programmed_action)
12817 #else
12818   if (game.gravity && !player->programmed_action)
12819 #endif
12820   {
12821     int jx = player->jx, jy = player->jy;
12822     boolean field_under_player_is_free =
12823       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12824     boolean player_is_standing_on_valid_field =
12825       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12826        (IS_WALKABLE(Feld[jx][jy]) &&
12827         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12828
12829     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12830       player->programmed_action = MV_DOWN;
12831   }
12832 }
12833
12834 /*
12835   MovePlayerOneStep()
12836   -----------------------------------------------------------------------------
12837   dx, dy:               direction (non-diagonal) to try to move the player to
12838   real_dx, real_dy:     direction as read from input device (can be diagonal)
12839 */
12840
12841 boolean MovePlayerOneStep(struct PlayerInfo *player,
12842                           int dx, int dy, int real_dx, int real_dy)
12843 {
12844   int jx = player->jx, jy = player->jy;
12845   int new_jx = jx + dx, new_jy = jy + dy;
12846 #if !USE_FIXED_DONT_RUN_INTO
12847   int element;
12848 #endif
12849   int can_move;
12850   boolean player_can_move = !player->cannot_move;
12851
12852   if (!player->active || (!dx && !dy))
12853     return MP_NO_ACTION;
12854
12855   player->MovDir = (dx < 0 ? MV_LEFT :
12856                     dx > 0 ? MV_RIGHT :
12857                     dy < 0 ? MV_UP :
12858                     dy > 0 ? MV_DOWN :  MV_NONE);
12859
12860   if (!IN_LEV_FIELD(new_jx, new_jy))
12861     return MP_NO_ACTION;
12862
12863   if (!player_can_move)
12864   {
12865     if (player->MovPos == 0)
12866     {
12867       player->is_moving = FALSE;
12868       player->is_digging = FALSE;
12869       player->is_collecting = FALSE;
12870       player->is_snapping = FALSE;
12871       player->is_pushing = FALSE;
12872     }
12873   }
12874
12875 #if 1
12876   if (!options.network && game.centered_player_nr == -1 &&
12877       !AllPlayersInSight(player, new_jx, new_jy))
12878     return MP_NO_ACTION;
12879 #else
12880   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12881     return MP_NO_ACTION;
12882 #endif
12883
12884 #if !USE_FIXED_DONT_RUN_INTO
12885   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12886
12887   /* (moved to DigField()) */
12888   if (player_can_move && DONT_RUN_INTO(element))
12889   {
12890     if (element == EL_ACID && dx == 0 && dy == 1)
12891     {
12892       SplashAcid(new_jx, new_jy);
12893       Feld[jx][jy] = EL_PLAYER_1;
12894       InitMovingField(jx, jy, MV_DOWN);
12895       Store[jx][jy] = EL_ACID;
12896       ContinueMoving(jx, jy);
12897       BuryPlayer(player);
12898     }
12899     else
12900       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12901
12902     return MP_MOVING;
12903   }
12904 #endif
12905
12906   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12907   if (can_move != MP_MOVING)
12908     return can_move;
12909
12910   /* check if DigField() has caused relocation of the player */
12911   if (player->jx != jx || player->jy != jy)
12912     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12913
12914   StorePlayer[jx][jy] = 0;
12915   player->last_jx = jx;
12916   player->last_jy = jy;
12917   player->jx = new_jx;
12918   player->jy = new_jy;
12919   StorePlayer[new_jx][new_jy] = player->element_nr;
12920
12921   if (player->move_delay_value_next != -1)
12922   {
12923     player->move_delay_value = player->move_delay_value_next;
12924     player->move_delay_value_next = -1;
12925   }
12926
12927   player->MovPos =
12928     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12929
12930   player->step_counter++;
12931
12932   PlayerVisit[jx][jy] = FrameCounter;
12933
12934 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12935   player->is_moving = TRUE;
12936 #endif
12937
12938 #if 1
12939   /* should better be called in MovePlayer(), but this breaks some tapes */
12940   ScrollPlayer(player, SCROLL_INIT);
12941 #endif
12942
12943   return MP_MOVING;
12944 }
12945
12946 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12947 {
12948   int jx = player->jx, jy = player->jy;
12949   int old_jx = jx, old_jy = jy;
12950   int moved = MP_NO_ACTION;
12951
12952   if (!player->active)
12953     return FALSE;
12954
12955   if (!dx && !dy)
12956   {
12957     if (player->MovPos == 0)
12958     {
12959       player->is_moving = FALSE;
12960       player->is_digging = FALSE;
12961       player->is_collecting = FALSE;
12962       player->is_snapping = FALSE;
12963       player->is_pushing = FALSE;
12964     }
12965
12966     return FALSE;
12967   }
12968
12969   if (player->move_delay > 0)
12970     return FALSE;
12971
12972   player->move_delay = -1;              /* set to "uninitialized" value */
12973
12974   /* store if player is automatically moved to next field */
12975   player->is_auto_moving = (player->programmed_action != MV_NONE);
12976
12977   /* remove the last programmed player action */
12978   player->programmed_action = 0;
12979
12980   if (player->MovPos)
12981   {
12982     /* should only happen if pre-1.2 tape recordings are played */
12983     /* this is only for backward compatibility */
12984
12985     int original_move_delay_value = player->move_delay_value;
12986
12987 #if DEBUG
12988     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12989            tape.counter);
12990 #endif
12991
12992     /* scroll remaining steps with finest movement resolution */
12993     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12994
12995     while (player->MovPos)
12996     {
12997       ScrollPlayer(player, SCROLL_GO_ON);
12998       ScrollScreen(NULL, SCROLL_GO_ON);
12999
13000       AdvanceFrameAndPlayerCounters(player->index_nr);
13001
13002       DrawAllPlayers();
13003       BackToFront();
13004     }
13005
13006     player->move_delay_value = original_move_delay_value;
13007   }
13008
13009   player->is_active = FALSE;
13010
13011   if (player->last_move_dir & MV_HORIZONTAL)
13012   {
13013     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13014       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13015   }
13016   else
13017   {
13018     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13019       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13020   }
13021
13022 #if USE_FIXED_BORDER_RUNNING_GFX
13023   if (!moved && !player->is_active)
13024   {
13025     player->is_moving = FALSE;
13026     player->is_digging = FALSE;
13027     player->is_collecting = FALSE;
13028     player->is_snapping = FALSE;
13029     player->is_pushing = FALSE;
13030   }
13031 #endif
13032
13033   jx = player->jx;
13034   jy = player->jy;
13035
13036 #if 1
13037   if (moved & MP_MOVING && !ScreenMovPos &&
13038       (player->index_nr == game.centered_player_nr ||
13039        game.centered_player_nr == -1))
13040 #else
13041   if (moved & MP_MOVING && !ScreenMovPos &&
13042       (player == local_player || !options.network))
13043 #endif
13044   {
13045     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13046     int offset = game.scroll_delay_value;
13047
13048     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13049     {
13050       /* actual player has left the screen -- scroll in that direction */
13051       if (jx != old_jx)         /* player has moved horizontally */
13052         scroll_x += (jx - old_jx);
13053       else                      /* player has moved vertically */
13054         scroll_y += (jy - old_jy);
13055     }
13056     else
13057     {
13058       if (jx != old_jx)         /* player has moved horizontally */
13059       {
13060         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13061             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13062           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13063
13064         /* don't scroll over playfield boundaries */
13065         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13066           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13067
13068         /* don't scroll more than one field at a time */
13069         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13070
13071         /* don't scroll against the player's moving direction */
13072         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13073             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13074           scroll_x = old_scroll_x;
13075       }
13076       else                      /* player has moved vertically */
13077       {
13078         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13079             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13080           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13081
13082         /* don't scroll over playfield boundaries */
13083         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13084           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13085
13086         /* don't scroll more than one field at a time */
13087         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13088
13089         /* don't scroll against the player's moving direction */
13090         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13091             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13092           scroll_y = old_scroll_y;
13093       }
13094     }
13095
13096     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13097     {
13098 #if 1
13099       if (!options.network && game.centered_player_nr == -1 &&
13100           !AllPlayersInVisibleScreen())
13101       {
13102         scroll_x = old_scroll_x;
13103         scroll_y = old_scroll_y;
13104       }
13105       else
13106 #else
13107       if (!options.network && !AllPlayersInVisibleScreen())
13108       {
13109         scroll_x = old_scroll_x;
13110         scroll_y = old_scroll_y;
13111       }
13112       else
13113 #endif
13114       {
13115         ScrollScreen(player, SCROLL_INIT);
13116         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13117       }
13118     }
13119   }
13120
13121   player->StepFrame = 0;
13122
13123   if (moved & MP_MOVING)
13124   {
13125     if (old_jx != jx && old_jy == jy)
13126       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13127     else if (old_jx == jx && old_jy != jy)
13128       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13129
13130     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13131
13132     player->last_move_dir = player->MovDir;
13133     player->is_moving = TRUE;
13134     player->is_snapping = FALSE;
13135     player->is_switching = FALSE;
13136     player->is_dropping = FALSE;
13137     player->is_dropping_pressed = FALSE;
13138     player->drop_pressed_delay = 0;
13139
13140 #if 0
13141     /* should better be called here than above, but this breaks some tapes */
13142     ScrollPlayer(player, SCROLL_INIT);
13143 #endif
13144   }
13145   else
13146   {
13147     CheckGravityMovementWhenNotMoving(player);
13148
13149     player->is_moving = FALSE;
13150
13151     /* at this point, the player is allowed to move, but cannot move right now
13152        (e.g. because of something blocking the way) -- ensure that the player
13153        is also allowed to move in the next frame (in old versions before 3.1.1,
13154        the player was forced to wait again for eight frames before next try) */
13155
13156     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13157       player->move_delay = 0;   /* allow direct movement in the next frame */
13158   }
13159
13160   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13161     player->move_delay = player->move_delay_value;
13162
13163   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13164   {
13165     TestIfPlayerTouchesBadThing(jx, jy);
13166     TestIfPlayerTouchesCustomElement(jx, jy);
13167   }
13168
13169   if (!player->active)
13170     RemovePlayer(player);
13171
13172   return moved;
13173 }
13174
13175 void ScrollPlayer(struct PlayerInfo *player, int mode)
13176 {
13177   int jx = player->jx, jy = player->jy;
13178   int last_jx = player->last_jx, last_jy = player->last_jy;
13179   int move_stepsize = TILEX / player->move_delay_value;
13180
13181 #if USE_NEW_PLAYER_SPEED
13182   if (!player->active)
13183     return;
13184
13185   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13186     return;
13187 #else
13188   if (!player->active || player->MovPos == 0)
13189     return;
13190 #endif
13191
13192   if (mode == SCROLL_INIT)
13193   {
13194     player->actual_frame_counter = FrameCounter;
13195     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13196
13197     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13198         Feld[last_jx][last_jy] == EL_EMPTY)
13199     {
13200       int last_field_block_delay = 0;   /* start with no blocking at all */
13201       int block_delay_adjustment = player->block_delay_adjustment;
13202
13203       /* if player blocks last field, add delay for exactly one move */
13204       if (player->block_last_field)
13205       {
13206         last_field_block_delay += player->move_delay_value;
13207
13208         /* when blocking enabled, prevent moving up despite gravity */
13209 #if USE_PLAYER_GRAVITY
13210         if (player->gravity && player->MovDir == MV_UP)
13211           block_delay_adjustment = -1;
13212 #else
13213         if (game.gravity && player->MovDir == MV_UP)
13214           block_delay_adjustment = -1;
13215 #endif
13216       }
13217
13218       /* add block delay adjustment (also possible when not blocking) */
13219       last_field_block_delay += block_delay_adjustment;
13220
13221       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13222       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13223     }
13224
13225 #if USE_NEW_PLAYER_SPEED
13226     if (player->MovPos != 0)    /* player has not yet reached destination */
13227       return;
13228 #else
13229     return;
13230 #endif
13231   }
13232   else if (!FrameReached(&player->actual_frame_counter, 1))
13233     return;
13234
13235 #if USE_NEW_PLAYER_SPEED
13236   if (player->MovPos != 0)
13237   {
13238     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13239     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13240
13241     /* before DrawPlayer() to draw correct player graphic for this case */
13242     if (player->MovPos == 0)
13243       CheckGravityMovement(player);
13244   }
13245 #else
13246   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13247   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13248
13249   /* before DrawPlayer() to draw correct player graphic for this case */
13250   if (player->MovPos == 0)
13251     CheckGravityMovement(player);
13252 #endif
13253
13254   if (player->MovPos == 0)      /* player reached destination field */
13255   {
13256     if (player->move_delay_reset_counter > 0)
13257     {
13258       player->move_delay_reset_counter--;
13259
13260       if (player->move_delay_reset_counter == 0)
13261       {
13262         /* continue with normal speed after quickly moving through gate */
13263         HALVE_PLAYER_SPEED(player);
13264
13265         /* be able to make the next move without delay */
13266         player->move_delay = 0;
13267       }
13268     }
13269
13270     player->last_jx = jx;
13271     player->last_jy = jy;
13272
13273     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13274         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13275         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13276         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13277         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13278         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13279     {
13280       DrawPlayer(player);       /* needed here only to cleanup last field */
13281       RemovePlayer(player);
13282
13283       if (local_player->friends_still_needed == 0 ||
13284           IS_SP_ELEMENT(Feld[jx][jy]))
13285         PlayerWins(player);
13286     }
13287
13288     /* this breaks one level: "machine", level 000 */
13289     {
13290       int move_direction = player->MovDir;
13291       int enter_side = MV_DIR_OPPOSITE(move_direction);
13292       int leave_side = move_direction;
13293       int old_jx = last_jx;
13294       int old_jy = last_jy;
13295       int old_element = Feld[old_jx][old_jy];
13296       int new_element = Feld[jx][jy];
13297
13298       if (IS_CUSTOM_ELEMENT(old_element))
13299         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13300                                    CE_LEFT_BY_PLAYER,
13301                                    player->index_bit, leave_side);
13302
13303       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13304                                           CE_PLAYER_LEAVES_X,
13305                                           player->index_bit, leave_side);
13306
13307       if (IS_CUSTOM_ELEMENT(new_element))
13308         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13309                                    player->index_bit, enter_side);
13310
13311       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13312                                           CE_PLAYER_ENTERS_X,
13313                                           player->index_bit, enter_side);
13314
13315       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13316                                         CE_MOVE_OF_X, move_direction);
13317     }
13318
13319     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13320     {
13321       TestIfPlayerTouchesBadThing(jx, jy);
13322       TestIfPlayerTouchesCustomElement(jx, jy);
13323
13324       /* needed because pushed element has not yet reached its destination,
13325          so it would trigger a change event at its previous field location */
13326       if (!player->is_pushing)
13327         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13328
13329       if (!player->active)
13330         RemovePlayer(player);
13331     }
13332
13333     if (!local_player->LevelSolved && level.use_step_counter)
13334     {
13335       int i;
13336
13337       TimePlayed++;
13338
13339       if (TimeLeft > 0)
13340       {
13341         TimeLeft--;
13342
13343         if (TimeLeft <= 10 && setup.time_limit)
13344           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13345
13346 #if 1
13347         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13348
13349         DisplayGameControlValues();
13350 #else
13351         DrawGameValue_Time(TimeLeft);
13352 #endif
13353
13354         if (!TimeLeft && setup.time_limit)
13355           for (i = 0; i < MAX_PLAYERS; i++)
13356             KillPlayer(&stored_player[i]);
13357       }
13358 #if 1
13359       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13360       {
13361         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13362
13363         DisplayGameControlValues();
13364       }
13365 #else
13366       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13367         DrawGameValue_Time(TimePlayed);
13368 #endif
13369     }
13370
13371     if (tape.single_step && tape.recording && !tape.pausing &&
13372         !player->programmed_action)
13373       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13374   }
13375 }
13376
13377 void ScrollScreen(struct PlayerInfo *player, int mode)
13378 {
13379   static unsigned long screen_frame_counter = 0;
13380
13381   if (mode == SCROLL_INIT)
13382   {
13383     /* set scrolling step size according to actual player's moving speed */
13384     ScrollStepSize = TILEX / player->move_delay_value;
13385
13386     screen_frame_counter = FrameCounter;
13387     ScreenMovDir = player->MovDir;
13388     ScreenMovPos = player->MovPos;
13389     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13390     return;
13391   }
13392   else if (!FrameReached(&screen_frame_counter, 1))
13393     return;
13394
13395   if (ScreenMovPos)
13396   {
13397     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13398     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13399     redraw_mask |= REDRAW_FIELD;
13400   }
13401   else
13402     ScreenMovDir = MV_NONE;
13403 }
13404
13405 void TestIfPlayerTouchesCustomElement(int x, int y)
13406 {
13407   static int xy[4][2] =
13408   {
13409     { 0, -1 },
13410     { -1, 0 },
13411     { +1, 0 },
13412     { 0, +1 }
13413   };
13414   static int trigger_sides[4][2] =
13415   {
13416     /* center side       border side */
13417     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13418     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13419     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13420     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13421   };
13422   static int touch_dir[4] =
13423   {
13424     MV_LEFT | MV_RIGHT,
13425     MV_UP   | MV_DOWN,
13426     MV_UP   | MV_DOWN,
13427     MV_LEFT | MV_RIGHT
13428   };
13429   int center_element = Feld[x][y];      /* should always be non-moving! */
13430   int i;
13431
13432   for (i = 0; i < NUM_DIRECTIONS; i++)
13433   {
13434     int xx = x + xy[i][0];
13435     int yy = y + xy[i][1];
13436     int center_side = trigger_sides[i][0];
13437     int border_side = trigger_sides[i][1];
13438     int border_element;
13439
13440     if (!IN_LEV_FIELD(xx, yy))
13441       continue;
13442
13443     if (IS_PLAYER(x, y))
13444     {
13445       struct PlayerInfo *player = PLAYERINFO(x, y);
13446
13447       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13448         border_element = Feld[xx][yy];          /* may be moving! */
13449       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13450         border_element = Feld[xx][yy];
13451       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13452         border_element = MovingOrBlocked2Element(xx, yy);
13453       else
13454         continue;               /* center and border element do not touch */
13455
13456       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13457                                  player->index_bit, border_side);
13458       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13459                                           CE_PLAYER_TOUCHES_X,
13460                                           player->index_bit, border_side);
13461     }
13462     else if (IS_PLAYER(xx, yy))
13463     {
13464       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13465
13466       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13467       {
13468         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13469           continue;             /* center and border element do not touch */
13470       }
13471
13472       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13473                                  player->index_bit, center_side);
13474       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13475                                           CE_PLAYER_TOUCHES_X,
13476                                           player->index_bit, center_side);
13477       break;
13478     }
13479   }
13480 }
13481
13482 #if USE_ELEMENT_TOUCHING_BUGFIX
13483
13484 void TestIfElementTouchesCustomElement(int x, int y)
13485 {
13486   static int xy[4][2] =
13487   {
13488     { 0, -1 },
13489     { -1, 0 },
13490     { +1, 0 },
13491     { 0, +1 }
13492   };
13493   static int trigger_sides[4][2] =
13494   {
13495     /* center side      border side */
13496     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13497     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13498     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13499     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13500   };
13501   static int touch_dir[4] =
13502   {
13503     MV_LEFT | MV_RIGHT,
13504     MV_UP   | MV_DOWN,
13505     MV_UP   | MV_DOWN,
13506     MV_LEFT | MV_RIGHT
13507   };
13508   boolean change_center_element = FALSE;
13509   int center_element = Feld[x][y];      /* should always be non-moving! */
13510   int border_element_old[NUM_DIRECTIONS];
13511   int i;
13512
13513   for (i = 0; i < NUM_DIRECTIONS; i++)
13514   {
13515     int xx = x + xy[i][0];
13516     int yy = y + xy[i][1];
13517     int border_element;
13518
13519     border_element_old[i] = -1;
13520
13521     if (!IN_LEV_FIELD(xx, yy))
13522       continue;
13523
13524     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13525       border_element = Feld[xx][yy];    /* may be moving! */
13526     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13527       border_element = Feld[xx][yy];
13528     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13529       border_element = MovingOrBlocked2Element(xx, yy);
13530     else
13531       continue;                 /* center and border element do not touch */
13532
13533     border_element_old[i] = border_element;
13534   }
13535
13536   for (i = 0; i < NUM_DIRECTIONS; i++)
13537   {
13538     int xx = x + xy[i][0];
13539     int yy = y + xy[i][1];
13540     int center_side = trigger_sides[i][0];
13541     int border_element = border_element_old[i];
13542
13543     if (border_element == -1)
13544       continue;
13545
13546     /* check for change of border element */
13547     CheckElementChangeBySide(xx, yy, border_element, center_element,
13548                              CE_TOUCHING_X, center_side);
13549   }
13550
13551   for (i = 0; i < NUM_DIRECTIONS; i++)
13552   {
13553     int border_side = trigger_sides[i][1];
13554     int border_element = border_element_old[i];
13555
13556     if (border_element == -1)
13557       continue;
13558
13559     /* check for change of center element (but change it only once) */
13560     if (!change_center_element)
13561       change_center_element =
13562         CheckElementChangeBySide(x, y, center_element, border_element,
13563                                  CE_TOUCHING_X, border_side);
13564   }
13565 }
13566
13567 #else
13568
13569 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13570 {
13571   static int xy[4][2] =
13572   {
13573     { 0, -1 },
13574     { -1, 0 },
13575     { +1, 0 },
13576     { 0, +1 }
13577   };
13578   static int trigger_sides[4][2] =
13579   {
13580     /* center side      border side */
13581     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13582     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13583     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13584     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13585   };
13586   static int touch_dir[4] =
13587   {
13588     MV_LEFT | MV_RIGHT,
13589     MV_UP   | MV_DOWN,
13590     MV_UP   | MV_DOWN,
13591     MV_LEFT | MV_RIGHT
13592   };
13593   boolean change_center_element = FALSE;
13594   int center_element = Feld[x][y];      /* should always be non-moving! */
13595   int i;
13596
13597   for (i = 0; i < NUM_DIRECTIONS; i++)
13598   {
13599     int xx = x + xy[i][0];
13600     int yy = y + xy[i][1];
13601     int center_side = trigger_sides[i][0];
13602     int border_side = trigger_sides[i][1];
13603     int border_element;
13604
13605     if (!IN_LEV_FIELD(xx, yy))
13606       continue;
13607
13608     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13609       border_element = Feld[xx][yy];    /* may be moving! */
13610     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13611       border_element = Feld[xx][yy];
13612     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13613       border_element = MovingOrBlocked2Element(xx, yy);
13614     else
13615       continue;                 /* center and border element do not touch */
13616
13617     /* check for change of center element (but change it only once) */
13618     if (!change_center_element)
13619       change_center_element =
13620         CheckElementChangeBySide(x, y, center_element, border_element,
13621                                  CE_TOUCHING_X, border_side);
13622
13623     /* check for change of border element */
13624     CheckElementChangeBySide(xx, yy, border_element, center_element,
13625                              CE_TOUCHING_X, center_side);
13626   }
13627 }
13628
13629 #endif
13630
13631 void TestIfElementHitsCustomElement(int x, int y, int direction)
13632 {
13633   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13634   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13635   int hitx = x + dx, hity = y + dy;
13636   int hitting_element = Feld[x][y];
13637   int touched_element;
13638
13639   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13640     return;
13641
13642   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13643                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13644
13645   if (IN_LEV_FIELD(hitx, hity))
13646   {
13647     int opposite_direction = MV_DIR_OPPOSITE(direction);
13648     int hitting_side = direction;
13649     int touched_side = opposite_direction;
13650     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13651                           MovDir[hitx][hity] != direction ||
13652                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13653
13654     object_hit = TRUE;
13655
13656     if (object_hit)
13657     {
13658       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13659                                CE_HITTING_X, touched_side);
13660
13661       CheckElementChangeBySide(hitx, hity, touched_element,
13662                                hitting_element, CE_HIT_BY_X, hitting_side);
13663
13664       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13665                                CE_HIT_BY_SOMETHING, opposite_direction);
13666     }
13667   }
13668
13669   /* "hitting something" is also true when hitting the playfield border */
13670   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13671                            CE_HITTING_SOMETHING, direction);
13672 }
13673
13674 #if 0
13675 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13676 {
13677   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13678   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13679   int hitx = x + dx, hity = y + dy;
13680   int hitting_element = Feld[x][y];
13681   int touched_element;
13682 #if 0
13683   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13684                         !IS_FREE(hitx, hity) &&
13685                         (!IS_MOVING(hitx, hity) ||
13686                          MovDir[hitx][hity] != direction ||
13687                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13688 #endif
13689
13690   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13691     return;
13692
13693 #if 0
13694   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13695     return;
13696 #endif
13697
13698   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13699                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13700
13701   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13702                            EP_CAN_SMASH_EVERYTHING, direction);
13703
13704   if (IN_LEV_FIELD(hitx, hity))
13705   {
13706     int opposite_direction = MV_DIR_OPPOSITE(direction);
13707     int hitting_side = direction;
13708     int touched_side = opposite_direction;
13709 #if 0
13710     int touched_element = MovingOrBlocked2Element(hitx, hity);
13711 #endif
13712 #if 1
13713     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13714                           MovDir[hitx][hity] != direction ||
13715                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13716
13717     object_hit = TRUE;
13718 #endif
13719
13720     if (object_hit)
13721     {
13722       int i;
13723
13724       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13725                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13726
13727       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13728                                CE_OTHER_IS_SMASHING, touched_side);
13729
13730       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13731                                CE_OTHER_GETS_SMASHED, hitting_side);
13732     }
13733   }
13734 }
13735 #endif
13736
13737 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13738 {
13739   int i, kill_x = -1, kill_y = -1;
13740
13741   int bad_element = -1;
13742   static int test_xy[4][2] =
13743   {
13744     { 0, -1 },
13745     { -1, 0 },
13746     { +1, 0 },
13747     { 0, +1 }
13748   };
13749   static int test_dir[4] =
13750   {
13751     MV_UP,
13752     MV_LEFT,
13753     MV_RIGHT,
13754     MV_DOWN
13755   };
13756
13757   for (i = 0; i < NUM_DIRECTIONS; i++)
13758   {
13759     int test_x, test_y, test_move_dir, test_element;
13760
13761     test_x = good_x + test_xy[i][0];
13762     test_y = good_y + test_xy[i][1];
13763
13764     if (!IN_LEV_FIELD(test_x, test_y))
13765       continue;
13766
13767     test_move_dir =
13768       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13769
13770     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13771
13772     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13773        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13774     */
13775     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13776         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13777     {
13778       kill_x = test_x;
13779       kill_y = test_y;
13780       bad_element = test_element;
13781
13782       break;
13783     }
13784   }
13785
13786   if (kill_x != -1 || kill_y != -1)
13787   {
13788     if (IS_PLAYER(good_x, good_y))
13789     {
13790       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13791
13792       if (player->shield_deadly_time_left > 0 &&
13793           !IS_INDESTRUCTIBLE(bad_element))
13794         Bang(kill_x, kill_y);
13795       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13796         KillPlayer(player);
13797     }
13798     else
13799       Bang(good_x, good_y);
13800   }
13801 }
13802
13803 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13804 {
13805   int i, kill_x = -1, kill_y = -1;
13806   int bad_element = Feld[bad_x][bad_y];
13807   static int test_xy[4][2] =
13808   {
13809     { 0, -1 },
13810     { -1, 0 },
13811     { +1, 0 },
13812     { 0, +1 }
13813   };
13814   static int touch_dir[4] =
13815   {
13816     MV_LEFT | MV_RIGHT,
13817     MV_UP   | MV_DOWN,
13818     MV_UP   | MV_DOWN,
13819     MV_LEFT | MV_RIGHT
13820   };
13821   static int test_dir[4] =
13822   {
13823     MV_UP,
13824     MV_LEFT,
13825     MV_RIGHT,
13826     MV_DOWN
13827   };
13828
13829   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13830     return;
13831
13832   for (i = 0; i < NUM_DIRECTIONS; i++)
13833   {
13834     int test_x, test_y, test_move_dir, test_element;
13835
13836     test_x = bad_x + test_xy[i][0];
13837     test_y = bad_y + test_xy[i][1];
13838     if (!IN_LEV_FIELD(test_x, test_y))
13839       continue;
13840
13841     test_move_dir =
13842       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13843
13844     test_element = Feld[test_x][test_y];
13845
13846     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13847        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13848     */
13849     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13850         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13851     {
13852       /* good thing is player or penguin that does not move away */
13853       if (IS_PLAYER(test_x, test_y))
13854       {
13855         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13856
13857         if (bad_element == EL_ROBOT && player->is_moving)
13858           continue;     /* robot does not kill player if he is moving */
13859
13860         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13861         {
13862           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13863             continue;           /* center and border element do not touch */
13864         }
13865
13866         kill_x = test_x;
13867         kill_y = test_y;
13868         break;
13869       }
13870       else if (test_element == EL_PENGUIN)
13871       {
13872         kill_x = test_x;
13873         kill_y = test_y;
13874         break;
13875       }
13876     }
13877   }
13878
13879   if (kill_x != -1 || kill_y != -1)
13880   {
13881     if (IS_PLAYER(kill_x, kill_y))
13882     {
13883       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13884
13885       if (player->shield_deadly_time_left > 0 &&
13886           !IS_INDESTRUCTIBLE(bad_element))
13887         Bang(bad_x, bad_y);
13888       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13889         KillPlayer(player);
13890     }
13891     else
13892       Bang(kill_x, kill_y);
13893   }
13894 }
13895
13896 void TestIfPlayerTouchesBadThing(int x, int y)
13897 {
13898   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13899 }
13900
13901 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13902 {
13903   TestIfGoodThingHitsBadThing(x, y, move_dir);
13904 }
13905
13906 void TestIfBadThingTouchesPlayer(int x, int y)
13907 {
13908   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13909 }
13910
13911 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13912 {
13913   TestIfBadThingHitsGoodThing(x, y, move_dir);
13914 }
13915
13916 void TestIfFriendTouchesBadThing(int x, int y)
13917 {
13918   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13919 }
13920
13921 void TestIfBadThingTouchesFriend(int x, int y)
13922 {
13923   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13924 }
13925
13926 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13927 {
13928   int i, kill_x = bad_x, kill_y = bad_y;
13929   static int xy[4][2] =
13930   {
13931     { 0, -1 },
13932     { -1, 0 },
13933     { +1, 0 },
13934     { 0, +1 }
13935   };
13936
13937   for (i = 0; i < NUM_DIRECTIONS; i++)
13938   {
13939     int x, y, element;
13940
13941     x = bad_x + xy[i][0];
13942     y = bad_y + xy[i][1];
13943     if (!IN_LEV_FIELD(x, y))
13944       continue;
13945
13946     element = Feld[x][y];
13947     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13948         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13949     {
13950       kill_x = x;
13951       kill_y = y;
13952       break;
13953     }
13954   }
13955
13956   if (kill_x != bad_x || kill_y != bad_y)
13957     Bang(bad_x, bad_y);
13958 }
13959
13960 void KillPlayer(struct PlayerInfo *player)
13961 {
13962   int jx = player->jx, jy = player->jy;
13963
13964   if (!player->active)
13965     return;
13966
13967   /* the following code was introduced to prevent an infinite loop when calling
13968      -> Bang()
13969      -> CheckTriggeredElementChangeExt()
13970      -> ExecuteCustomElementAction()
13971      -> KillPlayer()
13972      -> (infinitely repeating the above sequence of function calls)
13973      which occurs when killing the player while having a CE with the setting
13974      "kill player X when explosion of <player X>"; the solution using a new
13975      field "player->killed" was chosen for backwards compatibility, although
13976      clever use of the fields "player->active" etc. would probably also work */
13977 #if 1
13978   if (player->killed)
13979     return;
13980 #endif
13981
13982   player->killed = TRUE;
13983
13984   /* remove accessible field at the player's position */
13985   Feld[jx][jy] = EL_EMPTY;
13986
13987   /* deactivate shield (else Bang()/Explode() would not work right) */
13988   player->shield_normal_time_left = 0;
13989   player->shield_deadly_time_left = 0;
13990
13991   Bang(jx, jy);
13992   BuryPlayer(player);
13993 }
13994
13995 static void KillPlayerUnlessEnemyProtected(int x, int y)
13996 {
13997   if (!PLAYER_ENEMY_PROTECTED(x, y))
13998     KillPlayer(PLAYERINFO(x, y));
13999 }
14000
14001 static void KillPlayerUnlessExplosionProtected(int x, int y)
14002 {
14003   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14004     KillPlayer(PLAYERINFO(x, y));
14005 }
14006
14007 void BuryPlayer(struct PlayerInfo *player)
14008 {
14009   int jx = player->jx, jy = player->jy;
14010
14011   if (!player->active)
14012     return;
14013
14014   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14015   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14016
14017   player->GameOver = TRUE;
14018   RemovePlayer(player);
14019 }
14020
14021 void RemovePlayer(struct PlayerInfo *player)
14022 {
14023   int jx = player->jx, jy = player->jy;
14024   int i, found = FALSE;
14025
14026   player->present = FALSE;
14027   player->active = FALSE;
14028
14029   if (!ExplodeField[jx][jy])
14030     StorePlayer[jx][jy] = 0;
14031
14032   if (player->is_moving)
14033     TEST_DrawLevelField(player->last_jx, player->last_jy);
14034
14035   for (i = 0; i < MAX_PLAYERS; i++)
14036     if (stored_player[i].active)
14037       found = TRUE;
14038
14039   if (!found)
14040     AllPlayersGone = TRUE;
14041
14042   ExitX = ZX = jx;
14043   ExitY = ZY = jy;
14044 }
14045
14046 #if USE_NEW_SNAP_DELAY
14047 static void setFieldForSnapping(int x, int y, int element, int direction)
14048 {
14049   struct ElementInfo *ei = &element_info[element];
14050   int direction_bit = MV_DIR_TO_BIT(direction);
14051   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14052   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14053                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14054
14055   Feld[x][y] = EL_ELEMENT_SNAPPING;
14056   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14057
14058   ResetGfxAnimation(x, y);
14059
14060   GfxElement[x][y] = element;
14061   GfxAction[x][y] = action;
14062   GfxDir[x][y] = direction;
14063   GfxFrame[x][y] = -1;
14064 }
14065 #endif
14066
14067 /*
14068   =============================================================================
14069   checkDiagonalPushing()
14070   -----------------------------------------------------------------------------
14071   check if diagonal input device direction results in pushing of object
14072   (by checking if the alternative direction is walkable, diggable, ...)
14073   =============================================================================
14074 */
14075
14076 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14077                                     int x, int y, int real_dx, int real_dy)
14078 {
14079   int jx, jy, dx, dy, xx, yy;
14080
14081   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14082     return TRUE;
14083
14084   /* diagonal direction: check alternative direction */
14085   jx = player->jx;
14086   jy = player->jy;
14087   dx = x - jx;
14088   dy = y - jy;
14089   xx = jx + (dx == 0 ? real_dx : 0);
14090   yy = jy + (dy == 0 ? real_dy : 0);
14091
14092   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14093 }
14094
14095 /*
14096   =============================================================================
14097   DigField()
14098   -----------------------------------------------------------------------------
14099   x, y:                 field next to player (non-diagonal) to try to dig to
14100   real_dx, real_dy:     direction as read from input device (can be diagonal)
14101   =============================================================================
14102 */
14103
14104 static int DigField(struct PlayerInfo *player,
14105                     int oldx, int oldy, int x, int y,
14106                     int real_dx, int real_dy, int mode)
14107 {
14108   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14109   boolean player_was_pushing = player->is_pushing;
14110   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14111   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14112   int jx = oldx, jy = oldy;
14113   int dx = x - jx, dy = y - jy;
14114   int nextx = x + dx, nexty = y + dy;
14115   int move_direction = (dx == -1 ? MV_LEFT  :
14116                         dx == +1 ? MV_RIGHT :
14117                         dy == -1 ? MV_UP    :
14118                         dy == +1 ? MV_DOWN  : MV_NONE);
14119   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14120   int dig_side = MV_DIR_OPPOSITE(move_direction);
14121   int old_element = Feld[jx][jy];
14122 #if USE_FIXED_DONT_RUN_INTO
14123   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14124 #else
14125   int element;
14126 #endif
14127   int collect_count;
14128
14129   if (is_player)                /* function can also be called by EL_PENGUIN */
14130   {
14131     if (player->MovPos == 0)
14132     {
14133       player->is_digging = FALSE;
14134       player->is_collecting = FALSE;
14135     }
14136
14137     if (player->MovPos == 0)    /* last pushing move finished */
14138       player->is_pushing = FALSE;
14139
14140     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14141     {
14142       player->is_switching = FALSE;
14143       player->push_delay = -1;
14144
14145       return MP_NO_ACTION;
14146     }
14147   }
14148
14149 #if !USE_FIXED_DONT_RUN_INTO
14150   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14151     return MP_NO_ACTION;
14152 #endif
14153
14154   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14155     old_element = Back[jx][jy];
14156
14157   /* in case of element dropped at player position, check background */
14158   else if (Back[jx][jy] != EL_EMPTY &&
14159            game.engine_version >= VERSION_IDENT(2,2,0,0))
14160     old_element = Back[jx][jy];
14161
14162   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14163     return MP_NO_ACTION;        /* field has no opening in this direction */
14164
14165   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14166     return MP_NO_ACTION;        /* field has no opening in this direction */
14167
14168 #if USE_FIXED_DONT_RUN_INTO
14169   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14170   {
14171     SplashAcid(x, y);
14172
14173     Feld[jx][jy] = player->artwork_element;
14174     InitMovingField(jx, jy, MV_DOWN);
14175     Store[jx][jy] = EL_ACID;
14176     ContinueMoving(jx, jy);
14177     BuryPlayer(player);
14178
14179     return MP_DONT_RUN_INTO;
14180   }
14181 #endif
14182
14183 #if USE_FIXED_DONT_RUN_INTO
14184   if (player_can_move && DONT_RUN_INTO(element))
14185   {
14186     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14187
14188     return MP_DONT_RUN_INTO;
14189   }
14190 #endif
14191
14192 #if USE_FIXED_DONT_RUN_INTO
14193   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14194     return MP_NO_ACTION;
14195 #endif
14196
14197 #if !USE_FIXED_DONT_RUN_INTO
14198   element = Feld[x][y];
14199 #endif
14200
14201   collect_count = element_info[element].collect_count_initial;
14202
14203   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14204     return MP_NO_ACTION;
14205
14206   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14207     player_can_move = player_can_move_or_snap;
14208
14209   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14210       game.engine_version >= VERSION_IDENT(2,2,0,0))
14211   {
14212     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14213                                player->index_bit, dig_side);
14214     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14215                                         player->index_bit, dig_side);
14216
14217     if (element == EL_DC_LANDMINE)
14218       Bang(x, y);
14219
14220     if (Feld[x][y] != element)          /* field changed by snapping */
14221       return MP_ACTION;
14222
14223     return MP_NO_ACTION;
14224   }
14225
14226 #if USE_PLAYER_GRAVITY
14227   if (player->gravity && is_player && !player->is_auto_moving &&
14228       canFallDown(player) && move_direction != MV_DOWN &&
14229       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14230     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14231 #else
14232   if (game.gravity && is_player && !player->is_auto_moving &&
14233       canFallDown(player) && move_direction != MV_DOWN &&
14234       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14235     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14236 #endif
14237
14238   if (player_can_move &&
14239       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14240   {
14241     int sound_element = SND_ELEMENT(element);
14242     int sound_action = ACTION_WALKING;
14243
14244     if (IS_RND_GATE(element))
14245     {
14246       if (!player->key[RND_GATE_NR(element)])
14247         return MP_NO_ACTION;
14248     }
14249     else if (IS_RND_GATE_GRAY(element))
14250     {
14251       if (!player->key[RND_GATE_GRAY_NR(element)])
14252         return MP_NO_ACTION;
14253     }
14254     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14255     {
14256       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14257         return MP_NO_ACTION;
14258     }
14259     else if (element == EL_EXIT_OPEN ||
14260              element == EL_EM_EXIT_OPEN ||
14261              element == EL_STEEL_EXIT_OPEN ||
14262              element == EL_EM_STEEL_EXIT_OPEN ||
14263              element == EL_SP_EXIT_OPEN ||
14264              element == EL_SP_EXIT_OPENING)
14265     {
14266       sound_action = ACTION_PASSING;    /* player is passing exit */
14267     }
14268     else if (element == EL_EMPTY)
14269     {
14270       sound_action = ACTION_MOVING;             /* nothing to walk on */
14271     }
14272
14273     /* play sound from background or player, whatever is available */
14274     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14275       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14276     else
14277       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14278   }
14279   else if (player_can_move &&
14280            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14281   {
14282     if (!ACCESS_FROM(element, opposite_direction))
14283       return MP_NO_ACTION;      /* field not accessible from this direction */
14284
14285     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14286       return MP_NO_ACTION;
14287
14288     if (IS_EM_GATE(element))
14289     {
14290       if (!player->key[EM_GATE_NR(element)])
14291         return MP_NO_ACTION;
14292     }
14293     else if (IS_EM_GATE_GRAY(element))
14294     {
14295       if (!player->key[EM_GATE_GRAY_NR(element)])
14296         return MP_NO_ACTION;
14297     }
14298     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14299     {
14300       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14301         return MP_NO_ACTION;
14302     }
14303     else if (IS_EMC_GATE(element))
14304     {
14305       if (!player->key[EMC_GATE_NR(element)])
14306         return MP_NO_ACTION;
14307     }
14308     else if (IS_EMC_GATE_GRAY(element))
14309     {
14310       if (!player->key[EMC_GATE_GRAY_NR(element)])
14311         return MP_NO_ACTION;
14312     }
14313     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14314     {
14315       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14316         return MP_NO_ACTION;
14317     }
14318     else if (element == EL_DC_GATE_WHITE ||
14319              element == EL_DC_GATE_WHITE_GRAY ||
14320              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14321     {
14322       if (player->num_white_keys == 0)
14323         return MP_NO_ACTION;
14324
14325       player->num_white_keys--;
14326     }
14327     else if (IS_SP_PORT(element))
14328     {
14329       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14330           element == EL_SP_GRAVITY_PORT_RIGHT ||
14331           element == EL_SP_GRAVITY_PORT_UP ||
14332           element == EL_SP_GRAVITY_PORT_DOWN)
14333 #if USE_PLAYER_GRAVITY
14334         player->gravity = !player->gravity;
14335 #else
14336         game.gravity = !game.gravity;
14337 #endif
14338       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14339                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14340                element == EL_SP_GRAVITY_ON_PORT_UP ||
14341                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14342 #if USE_PLAYER_GRAVITY
14343         player->gravity = TRUE;
14344 #else
14345         game.gravity = TRUE;
14346 #endif
14347       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14348                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14349                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14350                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14351 #if USE_PLAYER_GRAVITY
14352         player->gravity = FALSE;
14353 #else
14354         game.gravity = FALSE;
14355 #endif
14356     }
14357
14358     /* automatically move to the next field with double speed */
14359     player->programmed_action = move_direction;
14360
14361     if (player->move_delay_reset_counter == 0)
14362     {
14363       player->move_delay_reset_counter = 2;     /* two double speed steps */
14364
14365       DOUBLE_PLAYER_SPEED(player);
14366     }
14367
14368     PlayLevelSoundAction(x, y, ACTION_PASSING);
14369   }
14370   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14371   {
14372     RemoveField(x, y);
14373
14374     if (mode != DF_SNAP)
14375     {
14376       GfxElement[x][y] = GFX_ELEMENT(element);
14377       player->is_digging = TRUE;
14378     }
14379
14380     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14381
14382     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14383                                         player->index_bit, dig_side);
14384
14385     if (mode == DF_SNAP)
14386     {
14387 #if USE_NEW_SNAP_DELAY
14388       if (level.block_snap_field)
14389         setFieldForSnapping(x, y, element, move_direction);
14390       else
14391         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14392 #else
14393       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14394 #endif
14395
14396       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14397                                           player->index_bit, dig_side);
14398     }
14399   }
14400   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14401   {
14402     RemoveField(x, y);
14403
14404     if (is_player && mode != DF_SNAP)
14405     {
14406       GfxElement[x][y] = element;
14407       player->is_collecting = TRUE;
14408     }
14409
14410     if (element == EL_SPEED_PILL)
14411     {
14412       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14413     }
14414     else if (element == EL_EXTRA_TIME && level.time > 0)
14415     {
14416       TimeLeft += level.extra_time;
14417
14418 #if 1
14419       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14420
14421       DisplayGameControlValues();
14422 #else
14423       DrawGameValue_Time(TimeLeft);
14424 #endif
14425     }
14426     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14427     {
14428       player->shield_normal_time_left += level.shield_normal_time;
14429       if (element == EL_SHIELD_DEADLY)
14430         player->shield_deadly_time_left += level.shield_deadly_time;
14431     }
14432     else if (element == EL_DYNAMITE ||
14433              element == EL_EM_DYNAMITE ||
14434              element == EL_SP_DISK_RED)
14435     {
14436       if (player->inventory_size < MAX_INVENTORY_SIZE)
14437         player->inventory_element[player->inventory_size++] = element;
14438
14439       DrawGameDoorValues();
14440     }
14441     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14442     {
14443       player->dynabomb_count++;
14444       player->dynabombs_left++;
14445     }
14446     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14447     {
14448       player->dynabomb_size++;
14449     }
14450     else if (element == EL_DYNABOMB_INCREASE_POWER)
14451     {
14452       player->dynabomb_xl = TRUE;
14453     }
14454     else if (IS_KEY(element))
14455     {
14456       player->key[KEY_NR(element)] = TRUE;
14457
14458       DrawGameDoorValues();
14459     }
14460     else if (element == EL_DC_KEY_WHITE)
14461     {
14462       player->num_white_keys++;
14463
14464       /* display white keys? */
14465       /* DrawGameDoorValues(); */
14466     }
14467     else if (IS_ENVELOPE(element))
14468     {
14469       player->show_envelope = element;
14470     }
14471     else if (element == EL_EMC_LENSES)
14472     {
14473       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14474
14475       RedrawAllInvisibleElementsForLenses();
14476     }
14477     else if (element == EL_EMC_MAGNIFIER)
14478     {
14479       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14480
14481       RedrawAllInvisibleElementsForMagnifier();
14482     }
14483     else if (IS_DROPPABLE(element) ||
14484              IS_THROWABLE(element))     /* can be collected and dropped */
14485     {
14486       int i;
14487
14488       if (collect_count == 0)
14489         player->inventory_infinite_element = element;
14490       else
14491         for (i = 0; i < collect_count; i++)
14492           if (player->inventory_size < MAX_INVENTORY_SIZE)
14493             player->inventory_element[player->inventory_size++] = element;
14494
14495       DrawGameDoorValues();
14496     }
14497     else if (collect_count > 0)
14498     {
14499       local_player->gems_still_needed -= collect_count;
14500       if (local_player->gems_still_needed < 0)
14501         local_player->gems_still_needed = 0;
14502
14503 #if 1
14504       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14505
14506       DisplayGameControlValues();
14507 #else
14508       DrawGameValue_Emeralds(local_player->gems_still_needed);
14509 #endif
14510     }
14511
14512     RaiseScoreElement(element);
14513     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14514
14515     if (is_player)
14516       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14517                                           player->index_bit, dig_side);
14518
14519     if (mode == DF_SNAP)
14520     {
14521 #if USE_NEW_SNAP_DELAY
14522       if (level.block_snap_field)
14523         setFieldForSnapping(x, y, element, move_direction);
14524       else
14525         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14526 #else
14527       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14528 #endif
14529
14530       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14531                                           player->index_bit, dig_side);
14532     }
14533   }
14534   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14535   {
14536     if (mode == DF_SNAP && element != EL_BD_ROCK)
14537       return MP_NO_ACTION;
14538
14539     if (CAN_FALL(element) && dy)
14540       return MP_NO_ACTION;
14541
14542     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14543         !(element == EL_SPRING && level.use_spring_bug))
14544       return MP_NO_ACTION;
14545
14546     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14547         ((move_direction & MV_VERTICAL &&
14548           ((element_info[element].move_pattern & MV_LEFT &&
14549             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14550            (element_info[element].move_pattern & MV_RIGHT &&
14551             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14552          (move_direction & MV_HORIZONTAL &&
14553           ((element_info[element].move_pattern & MV_UP &&
14554             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14555            (element_info[element].move_pattern & MV_DOWN &&
14556             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14557       return MP_NO_ACTION;
14558
14559     /* do not push elements already moving away faster than player */
14560     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14561         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14562       return MP_NO_ACTION;
14563
14564     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14565     {
14566       if (player->push_delay_value == -1 || !player_was_pushing)
14567         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14568     }
14569     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14570     {
14571       if (player->push_delay_value == -1)
14572         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14573     }
14574     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14575     {
14576       if (!player->is_pushing)
14577         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14578     }
14579
14580     player->is_pushing = TRUE;
14581     player->is_active = TRUE;
14582
14583     if (!(IN_LEV_FIELD(nextx, nexty) &&
14584           (IS_FREE(nextx, nexty) ||
14585            (IS_SB_ELEMENT(element) &&
14586             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14587            (IS_CUSTOM_ELEMENT(element) &&
14588             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14589       return MP_NO_ACTION;
14590
14591     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14592       return MP_NO_ACTION;
14593
14594     if (player->push_delay == -1)       /* new pushing; restart delay */
14595       player->push_delay = 0;
14596
14597     if (player->push_delay < player->push_delay_value &&
14598         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14599         element != EL_SPRING && element != EL_BALLOON)
14600     {
14601       /* make sure that there is no move delay before next try to push */
14602       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14603         player->move_delay = 0;
14604
14605       return MP_NO_ACTION;
14606     }
14607
14608     if (IS_CUSTOM_ELEMENT(element) &&
14609         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14610     {
14611       if (!DigFieldByCE(nextx, nexty, element))
14612         return MP_NO_ACTION;
14613     }
14614
14615     if (IS_SB_ELEMENT(element))
14616     {
14617       if (element == EL_SOKOBAN_FIELD_FULL)
14618       {
14619         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14620         local_player->sokobanfields_still_needed++;
14621       }
14622
14623       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14624       {
14625         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14626         local_player->sokobanfields_still_needed--;
14627       }
14628
14629       Feld[x][y] = EL_SOKOBAN_OBJECT;
14630
14631       if (Back[x][y] == Back[nextx][nexty])
14632         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14633       else if (Back[x][y] != 0)
14634         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14635                                     ACTION_EMPTYING);
14636       else
14637         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14638                                     ACTION_FILLING);
14639
14640       if (local_player->sokobanfields_still_needed == 0 &&
14641           game.emulation == EMU_SOKOBAN)
14642       {
14643         PlayerWins(player);
14644
14645         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14646       }
14647     }
14648     else
14649       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14650
14651     InitMovingField(x, y, move_direction);
14652     GfxAction[x][y] = ACTION_PUSHING;
14653
14654     if (mode == DF_SNAP)
14655       ContinueMoving(x, y);
14656     else
14657       MovPos[x][y] = (dx != 0 ? dx : dy);
14658
14659     Pushed[x][y] = TRUE;
14660     Pushed[nextx][nexty] = TRUE;
14661
14662     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14663       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14664     else
14665       player->push_delay_value = -1;    /* get new value later */
14666
14667     /* check for element change _after_ element has been pushed */
14668     if (game.use_change_when_pushing_bug)
14669     {
14670       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14671                                  player->index_bit, dig_side);
14672       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14673                                           player->index_bit, dig_side);
14674     }
14675   }
14676   else if (IS_SWITCHABLE(element))
14677   {
14678     if (PLAYER_SWITCHING(player, x, y))
14679     {
14680       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14681                                           player->index_bit, dig_side);
14682
14683       return MP_ACTION;
14684     }
14685
14686     player->is_switching = TRUE;
14687     player->switch_x = x;
14688     player->switch_y = y;
14689
14690     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14691
14692     if (element == EL_ROBOT_WHEEL)
14693     {
14694       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14695       ZX = x;
14696       ZY = y;
14697
14698       game.robot_wheel_active = TRUE;
14699
14700       TEST_DrawLevelField(x, y);
14701     }
14702     else if (element == EL_SP_TERMINAL)
14703     {
14704       int xx, yy;
14705
14706       SCAN_PLAYFIELD(xx, yy)
14707       {
14708         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14709           Bang(xx, yy);
14710         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14711           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14712       }
14713     }
14714     else if (IS_BELT_SWITCH(element))
14715     {
14716       ToggleBeltSwitch(x, y);
14717     }
14718     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14719              element == EL_SWITCHGATE_SWITCH_DOWN ||
14720              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14721              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14722     {
14723       ToggleSwitchgateSwitch(x, y);
14724     }
14725     else if (element == EL_LIGHT_SWITCH ||
14726              element == EL_LIGHT_SWITCH_ACTIVE)
14727     {
14728       ToggleLightSwitch(x, y);
14729     }
14730     else if (element == EL_TIMEGATE_SWITCH ||
14731              element == EL_DC_TIMEGATE_SWITCH)
14732     {
14733       ActivateTimegateSwitch(x, y);
14734     }
14735     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14736              element == EL_BALLOON_SWITCH_RIGHT ||
14737              element == EL_BALLOON_SWITCH_UP    ||
14738              element == EL_BALLOON_SWITCH_DOWN  ||
14739              element == EL_BALLOON_SWITCH_NONE  ||
14740              element == EL_BALLOON_SWITCH_ANY)
14741     {
14742       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14743                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14744                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14745                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14746                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14747                              move_direction);
14748     }
14749     else if (element == EL_LAMP)
14750     {
14751       Feld[x][y] = EL_LAMP_ACTIVE;
14752       local_player->lights_still_needed--;
14753
14754       ResetGfxAnimation(x, y);
14755       TEST_DrawLevelField(x, y);
14756     }
14757     else if (element == EL_TIME_ORB_FULL)
14758     {
14759       Feld[x][y] = EL_TIME_ORB_EMPTY;
14760
14761       if (level.time > 0 || level.use_time_orb_bug)
14762       {
14763         TimeLeft += level.time_orb_time;
14764
14765 #if 1
14766         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14767
14768         DisplayGameControlValues();
14769 #else
14770         DrawGameValue_Time(TimeLeft);
14771 #endif
14772       }
14773
14774       ResetGfxAnimation(x, y);
14775       TEST_DrawLevelField(x, y);
14776     }
14777     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14778              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14779     {
14780       int xx, yy;
14781
14782       game.ball_state = !game.ball_state;
14783
14784       SCAN_PLAYFIELD(xx, yy)
14785       {
14786         int e = Feld[xx][yy];
14787
14788         if (game.ball_state)
14789         {
14790           if (e == EL_EMC_MAGIC_BALL)
14791             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14792           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14793             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14794         }
14795         else
14796         {
14797           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14798             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14799           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14800             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14801         }
14802       }
14803     }
14804
14805     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14806                                         player->index_bit, dig_side);
14807
14808     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14809                                         player->index_bit, dig_side);
14810
14811     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14812                                         player->index_bit, dig_side);
14813
14814     return MP_ACTION;
14815   }
14816   else
14817   {
14818     if (!PLAYER_SWITCHING(player, x, y))
14819     {
14820       player->is_switching = TRUE;
14821       player->switch_x = x;
14822       player->switch_y = y;
14823
14824       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14825                                  player->index_bit, dig_side);
14826       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14827                                           player->index_bit, dig_side);
14828
14829       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14830                                  player->index_bit, dig_side);
14831       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14832                                           player->index_bit, dig_side);
14833     }
14834
14835     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14836                                player->index_bit, dig_side);
14837     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14838                                         player->index_bit, dig_side);
14839
14840     return MP_NO_ACTION;
14841   }
14842
14843   player->push_delay = -1;
14844
14845   if (is_player)                /* function can also be called by EL_PENGUIN */
14846   {
14847     if (Feld[x][y] != element)          /* really digged/collected something */
14848     {
14849       player->is_collecting = !player->is_digging;
14850       player->is_active = TRUE;
14851     }
14852   }
14853
14854   return MP_MOVING;
14855 }
14856
14857 static boolean DigFieldByCE(int x, int y, int digging_element)
14858 {
14859   int element = Feld[x][y];
14860
14861   if (!IS_FREE(x, y))
14862   {
14863     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14864                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14865                   ACTION_BREAKING);
14866
14867     /* no element can dig solid indestructible elements */
14868     if (IS_INDESTRUCTIBLE(element) &&
14869         !IS_DIGGABLE(element) &&
14870         !IS_COLLECTIBLE(element))
14871       return FALSE;
14872
14873     if (AmoebaNr[x][y] &&
14874         (element == EL_AMOEBA_FULL ||
14875          element == EL_BD_AMOEBA ||
14876          element == EL_AMOEBA_GROWING))
14877     {
14878       AmoebaCnt[AmoebaNr[x][y]]--;
14879       AmoebaCnt2[AmoebaNr[x][y]]--;
14880     }
14881
14882     if (IS_MOVING(x, y))
14883       RemoveMovingField(x, y);
14884     else
14885     {
14886       RemoveField(x, y);
14887       TEST_DrawLevelField(x, y);
14888     }
14889
14890     /* if digged element was about to explode, prevent the explosion */
14891     ExplodeField[x][y] = EX_TYPE_NONE;
14892
14893     PlayLevelSoundAction(x, y, action);
14894   }
14895
14896   Store[x][y] = EL_EMPTY;
14897
14898 #if 1
14899   /* this makes it possible to leave the removed element again */
14900   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14901     Store[x][y] = element;
14902 #else
14903   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14904   {
14905     int move_leave_element = element_info[digging_element].move_leave_element;
14906
14907     /* this makes it possible to leave the removed element again */
14908     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
14909                    element : move_leave_element);
14910   }
14911 #endif
14912
14913   return TRUE;
14914 }
14915
14916 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14917 {
14918   int jx = player->jx, jy = player->jy;
14919   int x = jx + dx, y = jy + dy;
14920   int snap_direction = (dx == -1 ? MV_LEFT  :
14921                         dx == +1 ? MV_RIGHT :
14922                         dy == -1 ? MV_UP    :
14923                         dy == +1 ? MV_DOWN  : MV_NONE);
14924   boolean can_continue_snapping = (level.continuous_snapping &&
14925                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14926
14927   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14928     return FALSE;
14929
14930   if (!player->active || !IN_LEV_FIELD(x, y))
14931     return FALSE;
14932
14933   if (dx && dy)
14934     return FALSE;
14935
14936   if (!dx && !dy)
14937   {
14938     if (player->MovPos == 0)
14939       player->is_pushing = FALSE;
14940
14941     player->is_snapping = FALSE;
14942
14943     if (player->MovPos == 0)
14944     {
14945       player->is_moving = FALSE;
14946       player->is_digging = FALSE;
14947       player->is_collecting = FALSE;
14948     }
14949
14950     return FALSE;
14951   }
14952
14953 #if USE_NEW_CONTINUOUS_SNAPPING
14954   /* prevent snapping with already pressed snap key when not allowed */
14955   if (player->is_snapping && !can_continue_snapping)
14956     return FALSE;
14957 #else
14958   if (player->is_snapping)
14959     return FALSE;
14960 #endif
14961
14962   player->MovDir = snap_direction;
14963
14964   if (player->MovPos == 0)
14965   {
14966     player->is_moving = FALSE;
14967     player->is_digging = FALSE;
14968     player->is_collecting = FALSE;
14969   }
14970
14971   player->is_dropping = FALSE;
14972   player->is_dropping_pressed = FALSE;
14973   player->drop_pressed_delay = 0;
14974
14975   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14976     return FALSE;
14977
14978   player->is_snapping = TRUE;
14979   player->is_active = TRUE;
14980
14981   if (player->MovPos == 0)
14982   {
14983     player->is_moving = FALSE;
14984     player->is_digging = FALSE;
14985     player->is_collecting = FALSE;
14986   }
14987
14988   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14989     TEST_DrawLevelField(player->last_jx, player->last_jy);
14990
14991   TEST_DrawLevelField(x, y);
14992
14993   return TRUE;
14994 }
14995
14996 static boolean DropElement(struct PlayerInfo *player)
14997 {
14998   int old_element, new_element;
14999   int dropx = player->jx, dropy = player->jy;
15000   int drop_direction = player->MovDir;
15001   int drop_side = drop_direction;
15002 #if 1
15003   int drop_element = get_next_dropped_element(player);
15004 #else
15005   int drop_element = (player->inventory_size > 0 ?
15006                       player->inventory_element[player->inventory_size - 1] :
15007                       player->inventory_infinite_element != EL_UNDEFINED ?
15008                       player->inventory_infinite_element :
15009                       player->dynabombs_left > 0 ?
15010                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15011                       EL_UNDEFINED);
15012 #endif
15013
15014   player->is_dropping_pressed = TRUE;
15015
15016   /* do not drop an element on top of another element; when holding drop key
15017      pressed without moving, dropped element must move away before the next
15018      element can be dropped (this is especially important if the next element
15019      is dynamite, which can be placed on background for historical reasons) */
15020   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15021     return MP_ACTION;
15022
15023   if (IS_THROWABLE(drop_element))
15024   {
15025     dropx += GET_DX_FROM_DIR(drop_direction);
15026     dropy += GET_DY_FROM_DIR(drop_direction);
15027
15028     if (!IN_LEV_FIELD(dropx, dropy))
15029       return FALSE;
15030   }
15031
15032   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15033   new_element = drop_element;           /* default: no change when dropping */
15034
15035   /* check if player is active, not moving and ready to drop */
15036   if (!player->active || player->MovPos || player->drop_delay > 0)
15037     return FALSE;
15038
15039   /* check if player has anything that can be dropped */
15040   if (new_element == EL_UNDEFINED)
15041     return FALSE;
15042
15043   /* check if drop key was pressed long enough for EM style dynamite */
15044   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15045     return FALSE;
15046
15047   /* check if anything can be dropped at the current position */
15048   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15049     return FALSE;
15050
15051   /* collected custom elements can only be dropped on empty fields */
15052   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15053     return FALSE;
15054
15055   if (old_element != EL_EMPTY)
15056     Back[dropx][dropy] = old_element;   /* store old element on this field */
15057
15058   ResetGfxAnimation(dropx, dropy);
15059   ResetRandomAnimationValue(dropx, dropy);
15060
15061   if (player->inventory_size > 0 ||
15062       player->inventory_infinite_element != EL_UNDEFINED)
15063   {
15064     if (player->inventory_size > 0)
15065     {
15066       player->inventory_size--;
15067
15068       DrawGameDoorValues();
15069
15070       if (new_element == EL_DYNAMITE)
15071         new_element = EL_DYNAMITE_ACTIVE;
15072       else if (new_element == EL_EM_DYNAMITE)
15073         new_element = EL_EM_DYNAMITE_ACTIVE;
15074       else if (new_element == EL_SP_DISK_RED)
15075         new_element = EL_SP_DISK_RED_ACTIVE;
15076     }
15077
15078     Feld[dropx][dropy] = new_element;
15079
15080     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15081       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15082                           el2img(Feld[dropx][dropy]), 0);
15083
15084     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15085
15086     /* needed if previous element just changed to "empty" in the last frame */
15087     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15088
15089     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15090                                player->index_bit, drop_side);
15091     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15092                                         CE_PLAYER_DROPS_X,
15093                                         player->index_bit, drop_side);
15094
15095     TestIfElementTouchesCustomElement(dropx, dropy);
15096   }
15097   else          /* player is dropping a dyna bomb */
15098   {
15099     player->dynabombs_left--;
15100
15101     Feld[dropx][dropy] = new_element;
15102
15103     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15104       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15105                           el2img(Feld[dropx][dropy]), 0);
15106
15107     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15108   }
15109
15110   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15111     InitField_WithBug1(dropx, dropy, FALSE);
15112
15113   new_element = Feld[dropx][dropy];     /* element might have changed */
15114
15115   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15116       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15117   {
15118     int move_direction, nextx, nexty;
15119
15120     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15121       MovDir[dropx][dropy] = drop_direction;
15122
15123     move_direction = MovDir[dropx][dropy];
15124     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15125     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15126
15127     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15128
15129 #if USE_FIX_IMPACT_COLLISION
15130     /* do not cause impact style collision by dropping elements that can fall */
15131     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15132 #else
15133     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15134 #endif
15135   }
15136
15137   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15138   player->is_dropping = TRUE;
15139
15140   player->drop_pressed_delay = 0;
15141   player->is_dropping_pressed = FALSE;
15142
15143   player->drop_x = dropx;
15144   player->drop_y = dropy;
15145
15146   return TRUE;
15147 }
15148
15149 /* ------------------------------------------------------------------------- */
15150 /* game sound playing functions                                              */
15151 /* ------------------------------------------------------------------------- */
15152
15153 static int *loop_sound_frame = NULL;
15154 static int *loop_sound_volume = NULL;
15155
15156 void InitPlayLevelSound()
15157 {
15158   int num_sounds = getSoundListSize();
15159
15160   checked_free(loop_sound_frame);
15161   checked_free(loop_sound_volume);
15162
15163   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15164   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15165 }
15166
15167 static void PlayLevelSound(int x, int y, int nr)
15168 {
15169   int sx = SCREENX(x), sy = SCREENY(y);
15170   int volume, stereo_position;
15171   int max_distance = 8;
15172   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15173
15174   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15175       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15176     return;
15177
15178   if (!IN_LEV_FIELD(x, y) ||
15179       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15180       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15181     return;
15182
15183   volume = SOUND_MAX_VOLUME;
15184
15185   if (!IN_SCR_FIELD(sx, sy))
15186   {
15187     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15188     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15189
15190     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15191   }
15192
15193   stereo_position = (SOUND_MAX_LEFT +
15194                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15195                      (SCR_FIELDX + 2 * max_distance));
15196
15197   if (IS_LOOP_SOUND(nr))
15198   {
15199     /* This assures that quieter loop sounds do not overwrite louder ones,
15200        while restarting sound volume comparison with each new game frame. */
15201
15202     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15203       return;
15204
15205     loop_sound_volume[nr] = volume;
15206     loop_sound_frame[nr] = FrameCounter;
15207   }
15208
15209   PlaySoundExt(nr, volume, stereo_position, type);
15210 }
15211
15212 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15213 {
15214   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15215                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15216                  y < LEVELY(BY1) ? LEVELY(BY1) :
15217                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15218                  sound_action);
15219 }
15220
15221 static void PlayLevelSoundAction(int x, int y, int action)
15222 {
15223   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15224 }
15225
15226 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15227 {
15228   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15229
15230   if (sound_effect != SND_UNDEFINED)
15231     PlayLevelSound(x, y, sound_effect);
15232 }
15233
15234 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15235                                               int action)
15236 {
15237   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15238
15239   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15240     PlayLevelSound(x, y, sound_effect);
15241 }
15242
15243 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15244 {
15245   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15246
15247   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15248     PlayLevelSound(x, y, sound_effect);
15249 }
15250
15251 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15252 {
15253   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15254
15255   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15256     StopSound(sound_effect);
15257 }
15258
15259 static void PlayLevelMusic()
15260 {
15261   if (levelset.music[level_nr] != MUS_UNDEFINED)
15262     PlayMusic(levelset.music[level_nr]);        /* from config file */
15263   else
15264     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15265 }
15266
15267 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15268 {
15269   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15270   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15271   int x = xx - 1 - offset;
15272   int y = yy - 1 - offset;
15273
15274   switch (sample)
15275   {
15276     case SAMPLE_blank:
15277       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15278       break;
15279
15280     case SAMPLE_roll:
15281       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15282       break;
15283
15284     case SAMPLE_stone:
15285       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15286       break;
15287
15288     case SAMPLE_nut:
15289       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15290       break;
15291
15292     case SAMPLE_crack:
15293       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15294       break;
15295
15296     case SAMPLE_bug:
15297       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15298       break;
15299
15300     case SAMPLE_tank:
15301       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15302       break;
15303
15304     case SAMPLE_android_clone:
15305       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15306       break;
15307
15308     case SAMPLE_android_move:
15309       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15310       break;
15311
15312     case SAMPLE_spring:
15313       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15314       break;
15315
15316     case SAMPLE_slurp:
15317       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15318       break;
15319
15320     case SAMPLE_eater:
15321       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15322       break;
15323
15324     case SAMPLE_eater_eat:
15325       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15326       break;
15327
15328     case SAMPLE_alien:
15329       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15330       break;
15331
15332     case SAMPLE_collect:
15333       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15334       break;
15335
15336     case SAMPLE_diamond:
15337       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15338       break;
15339
15340     case SAMPLE_squash:
15341       /* !!! CHECK THIS !!! */
15342 #if 1
15343       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15344 #else
15345       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15346 #endif
15347       break;
15348
15349     case SAMPLE_wonderfall:
15350       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15351       break;
15352
15353     case SAMPLE_drip:
15354       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15355       break;
15356
15357     case SAMPLE_push:
15358       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15359       break;
15360
15361     case SAMPLE_dirt:
15362       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15363       break;
15364
15365     case SAMPLE_acid:
15366       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15367       break;
15368
15369     case SAMPLE_ball:
15370       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15371       break;
15372
15373     case SAMPLE_grow:
15374       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15375       break;
15376
15377     case SAMPLE_wonder:
15378       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15379       break;
15380
15381     case SAMPLE_door:
15382       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15383       break;
15384
15385     case SAMPLE_exit_open:
15386       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15387       break;
15388
15389     case SAMPLE_exit_leave:
15390       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15391       break;
15392
15393     case SAMPLE_dynamite:
15394       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15395       break;
15396
15397     case SAMPLE_tick:
15398       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15399       break;
15400
15401     case SAMPLE_press:
15402       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15403       break;
15404
15405     case SAMPLE_wheel:
15406       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15407       break;
15408
15409     case SAMPLE_boom:
15410       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15411       break;
15412
15413     case SAMPLE_die:
15414       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15415       break;
15416
15417     case SAMPLE_time:
15418       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15419       break;
15420
15421     default:
15422       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15423       break;
15424   }
15425 }
15426
15427 #if 0
15428 void ChangeTime(int value)
15429 {
15430   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15431
15432   *time += value;
15433
15434   /* EMC game engine uses value from time counter of RND game engine */
15435   level.native_em_level->lev->time = *time;
15436
15437   DrawGameValue_Time(*time);
15438 }
15439
15440 void RaiseScore(int value)
15441 {
15442   /* EMC game engine and RND game engine have separate score counters */
15443   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15444                 &level.native_em_level->lev->score : &local_player->score);
15445
15446   *score += value;
15447
15448   DrawGameValue_Score(*score);
15449 }
15450 #endif
15451
15452 void RaiseScore(int value)
15453 {
15454   local_player->score += value;
15455
15456 #if 1
15457   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15458
15459   DisplayGameControlValues();
15460 #else
15461   DrawGameValue_Score(local_player->score);
15462 #endif
15463 }
15464
15465 void RaiseScoreElement(int element)
15466 {
15467   switch (element)
15468   {
15469     case EL_EMERALD:
15470     case EL_BD_DIAMOND:
15471     case EL_EMERALD_YELLOW:
15472     case EL_EMERALD_RED:
15473     case EL_EMERALD_PURPLE:
15474     case EL_SP_INFOTRON:
15475       RaiseScore(level.score[SC_EMERALD]);
15476       break;
15477     case EL_DIAMOND:
15478       RaiseScore(level.score[SC_DIAMOND]);
15479       break;
15480     case EL_CRYSTAL:
15481       RaiseScore(level.score[SC_CRYSTAL]);
15482       break;
15483     case EL_PEARL:
15484       RaiseScore(level.score[SC_PEARL]);
15485       break;
15486     case EL_BUG:
15487     case EL_BD_BUTTERFLY:
15488     case EL_SP_ELECTRON:
15489       RaiseScore(level.score[SC_BUG]);
15490       break;
15491     case EL_SPACESHIP:
15492     case EL_BD_FIREFLY:
15493     case EL_SP_SNIKSNAK:
15494       RaiseScore(level.score[SC_SPACESHIP]);
15495       break;
15496     case EL_YAMYAM:
15497     case EL_DARK_YAMYAM:
15498       RaiseScore(level.score[SC_YAMYAM]);
15499       break;
15500     case EL_ROBOT:
15501       RaiseScore(level.score[SC_ROBOT]);
15502       break;
15503     case EL_PACMAN:
15504       RaiseScore(level.score[SC_PACMAN]);
15505       break;
15506     case EL_NUT:
15507       RaiseScore(level.score[SC_NUT]);
15508       break;
15509     case EL_DYNAMITE:
15510     case EL_EM_DYNAMITE:
15511     case EL_SP_DISK_RED:
15512     case EL_DYNABOMB_INCREASE_NUMBER:
15513     case EL_DYNABOMB_INCREASE_SIZE:
15514     case EL_DYNABOMB_INCREASE_POWER:
15515       RaiseScore(level.score[SC_DYNAMITE]);
15516       break;
15517     case EL_SHIELD_NORMAL:
15518     case EL_SHIELD_DEADLY:
15519       RaiseScore(level.score[SC_SHIELD]);
15520       break;
15521     case EL_EXTRA_TIME:
15522       RaiseScore(level.extra_time_score);
15523       break;
15524     case EL_KEY_1:
15525     case EL_KEY_2:
15526     case EL_KEY_3:
15527     case EL_KEY_4:
15528     case EL_EM_KEY_1:
15529     case EL_EM_KEY_2:
15530     case EL_EM_KEY_3:
15531     case EL_EM_KEY_4:
15532     case EL_EMC_KEY_5:
15533     case EL_EMC_KEY_6:
15534     case EL_EMC_KEY_7:
15535     case EL_EMC_KEY_8:
15536     case EL_DC_KEY_WHITE:
15537       RaiseScore(level.score[SC_KEY]);
15538       break;
15539     default:
15540       RaiseScore(element_info[element].collect_score);
15541       break;
15542   }
15543 }
15544
15545 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15546 {
15547   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15548   {
15549 #if defined(NETWORK_AVALIABLE)
15550     if (options.network)
15551       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15552     else
15553 #endif
15554     {
15555       if (quick_quit)
15556       {
15557 #if 1
15558
15559 #if 1
15560         FadeSkipNextFadeIn();
15561 #else
15562         fading = fading_none;
15563 #endif
15564
15565 #else
15566         OpenDoor(DOOR_CLOSE_1);
15567 #endif
15568
15569         game_status = GAME_MODE_MAIN;
15570
15571 #if 1
15572         DrawAndFadeInMainMenu(REDRAW_FIELD);
15573 #else
15574         DrawMainMenu();
15575 #endif
15576       }
15577       else
15578       {
15579 #if 0
15580         FadeOut(REDRAW_FIELD);
15581 #endif
15582
15583         game_status = GAME_MODE_MAIN;
15584
15585         DrawAndFadeInMainMenu(REDRAW_FIELD);
15586       }
15587     }
15588   }
15589   else          /* continue playing the game */
15590   {
15591     if (tape.playing && tape.deactivate_display)
15592       TapeDeactivateDisplayOff(TRUE);
15593
15594     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15595
15596     if (tape.playing && tape.deactivate_display)
15597       TapeDeactivateDisplayOn();
15598   }
15599 }
15600
15601 void RequestQuitGame(boolean ask_if_really_quit)
15602 {
15603   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15604   boolean skip_request = AllPlayersGone || quick_quit;
15605
15606   RequestQuitGameExt(skip_request, quick_quit,
15607                      "Do you really want to quit the game ?");
15608 }
15609
15610
15611 /* ------------------------------------------------------------------------- */
15612 /* random generator functions                                                */
15613 /* ------------------------------------------------------------------------- */
15614
15615 unsigned int InitEngineRandom_RND(long seed)
15616 {
15617   game.num_random_calls = 0;
15618
15619 #if 0
15620   unsigned int rnd_seed = InitEngineRandom(seed);
15621
15622   printf("::: START RND: %d\n", rnd_seed);
15623
15624   return rnd_seed;
15625 #else
15626
15627   return InitEngineRandom(seed);
15628
15629 #endif
15630
15631 }
15632
15633 unsigned int RND(int max)
15634 {
15635   if (max > 0)
15636   {
15637     game.num_random_calls++;
15638
15639     return GetEngineRandom(max);
15640   }
15641
15642   return 0;
15643 }
15644
15645
15646 /* ------------------------------------------------------------------------- */
15647 /* game engine snapshot handling functions                                   */
15648 /* ------------------------------------------------------------------------- */
15649
15650 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15651
15652 struct EngineSnapshotInfo
15653 {
15654   /* runtime values for custom element collect score */
15655   int collect_score[NUM_CUSTOM_ELEMENTS];
15656
15657   /* runtime values for group element choice position */
15658   int choice_pos[NUM_GROUP_ELEMENTS];
15659
15660   /* runtime values for belt position animations */
15661   int belt_graphic[4 * NUM_BELT_PARTS];
15662   int belt_anim_mode[4 * NUM_BELT_PARTS];
15663 };
15664
15665 struct EngineSnapshotNodeInfo
15666 {
15667   void *buffer_orig;
15668   void *buffer_copy;
15669   int size;
15670 };
15671
15672 static struct EngineSnapshotInfo engine_snapshot_rnd;
15673 static ListNode *engine_snapshot_list = NULL;
15674 static char *snapshot_level_identifier = NULL;
15675 static int snapshot_level_nr = -1;
15676
15677 void FreeEngineSnapshot()
15678 {
15679   while (engine_snapshot_list != NULL)
15680     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15681                        checked_free);
15682
15683   setString(&snapshot_level_identifier, NULL);
15684   snapshot_level_nr = -1;
15685 }
15686
15687 static void SaveEngineSnapshotValues_RND()
15688 {
15689   static int belt_base_active_element[4] =
15690   {
15691     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15692     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15693     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15694     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15695   };
15696   int i, j;
15697
15698   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15699   {
15700     int element = EL_CUSTOM_START + i;
15701
15702     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15703   }
15704
15705   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15706   {
15707     int element = EL_GROUP_START + i;
15708
15709     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15710   }
15711
15712   for (i = 0; i < 4; i++)
15713   {
15714     for (j = 0; j < NUM_BELT_PARTS; j++)
15715     {
15716       int element = belt_base_active_element[i] + j;
15717       int graphic = el2img(element);
15718       int anim_mode = graphic_info[graphic].anim_mode;
15719
15720       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15721       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15722     }
15723   }
15724 }
15725
15726 static void LoadEngineSnapshotValues_RND()
15727 {
15728   unsigned long num_random_calls = game.num_random_calls;
15729   int i, j;
15730
15731   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15732   {
15733     int element = EL_CUSTOM_START + i;
15734
15735     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15736   }
15737
15738   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15739   {
15740     int element = EL_GROUP_START + i;
15741
15742     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15743   }
15744
15745   for (i = 0; i < 4; i++)
15746   {
15747     for (j = 0; j < NUM_BELT_PARTS; j++)
15748     {
15749       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15750       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15751
15752       graphic_info[graphic].anim_mode = anim_mode;
15753     }
15754   }
15755
15756   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15757   {
15758     InitRND(tape.random_seed);
15759     for (i = 0; i < num_random_calls; i++)
15760       RND(1);
15761   }
15762
15763   if (game.num_random_calls != num_random_calls)
15764   {
15765     Error(ERR_INFO, "number of random calls out of sync");
15766     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15767     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15768     Error(ERR_EXIT, "this should not happen -- please debug");
15769   }
15770 }
15771
15772 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15773 {
15774   struct EngineSnapshotNodeInfo *bi =
15775     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15776
15777   bi->buffer_orig = buffer;
15778   bi->buffer_copy = checked_malloc(size);
15779   bi->size = size;
15780
15781   memcpy(bi->buffer_copy, buffer, size);
15782
15783   addNodeToList(&engine_snapshot_list, NULL, bi);
15784 }
15785
15786 void SaveEngineSnapshot()
15787 {
15788   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15789
15790   if (level_editor_test_game)   /* do not save snapshots from editor */
15791     return;
15792
15793   /* copy some special values to a structure better suited for the snapshot */
15794
15795   SaveEngineSnapshotValues_RND();
15796   SaveEngineSnapshotValues_EM();
15797
15798   /* save values stored in special snapshot structure */
15799
15800   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15801   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15802
15803   /* save further RND engine values */
15804
15805   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15806   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15807   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15808
15809   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15810   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15811   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15812   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15813
15814   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15815   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15816   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15817   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15818   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15819
15820   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15821   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15822   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15823
15824   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15825
15826   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15827
15828   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15829   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15830
15831   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15832   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15833   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15834   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15835   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15836   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15837   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15838   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15839   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15840   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15841   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15842   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15843   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15844   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15845   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15846   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15847   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15848   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15849
15850   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15851   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15852
15853   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15854   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15855   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15856
15857   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15858   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15859
15860   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15861   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15862   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15863   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15864   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15865
15866   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15867   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15868
15869   /* save level identification information */
15870
15871   setString(&snapshot_level_identifier, leveldir_current->identifier);
15872   snapshot_level_nr = level_nr;
15873
15874 #if 0
15875   ListNode *node = engine_snapshot_list;
15876   int num_bytes = 0;
15877
15878   while (node != NULL)
15879   {
15880     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15881
15882     node = node->next;
15883   }
15884
15885   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15886 #endif
15887 }
15888
15889 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15890 {
15891   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15892 }
15893
15894 void LoadEngineSnapshot()
15895 {
15896   ListNode *node = engine_snapshot_list;
15897
15898   if (engine_snapshot_list == NULL)
15899     return;
15900
15901   while (node != NULL)
15902   {
15903     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15904
15905     node = node->next;
15906   }
15907
15908   /* restore special values from snapshot structure */
15909
15910   LoadEngineSnapshotValues_RND();
15911   LoadEngineSnapshotValues_EM();
15912 }
15913
15914 boolean CheckEngineSnapshot()
15915 {
15916   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15917           snapshot_level_nr == level_nr);
15918 }
15919
15920
15921 /* ---------- new game button stuff ---------------------------------------- */
15922
15923 /* graphic position values for game buttons */
15924 #define GAME_BUTTON_XSIZE       30
15925 #define GAME_BUTTON_YSIZE       30
15926 #define GAME_BUTTON_XPOS        5
15927 #define GAME_BUTTON_YPOS        215
15928 #define SOUND_BUTTON_XPOS       5
15929 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15930
15931 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15932 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15933 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15934 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15935 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15936 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15937
15938 static struct
15939 {
15940   int *x, *y;
15941   int gd_x, gd_y;
15942   int gadget_id;
15943   char *infotext;
15944 } gamebutton_info[NUM_GAME_BUTTONS] =
15945 {
15946 #if 1
15947   {
15948     &game.button.stop.x,        &game.button.stop.y,
15949     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15950     GAME_CTRL_ID_STOP,
15951     "stop game"
15952   },
15953   {
15954     &game.button.pause.x,       &game.button.pause.y,
15955     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15956     GAME_CTRL_ID_PAUSE,
15957     "pause game"
15958   },
15959   {
15960     &game.button.play.x,        &game.button.play.y,
15961     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15962     GAME_CTRL_ID_PLAY,
15963     "play game"
15964   },
15965   {
15966     &game.button.sound_music.x, &game.button.sound_music.y,
15967     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15968     SOUND_CTRL_ID_MUSIC,
15969     "background music on/off"
15970   },
15971   {
15972     &game.button.sound_loops.x, &game.button.sound_loops.y,
15973     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15974     SOUND_CTRL_ID_LOOPS,
15975     "sound loops on/off"
15976   },
15977   {
15978     &game.button.sound_simple.x,&game.button.sound_simple.y,
15979     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15980     SOUND_CTRL_ID_SIMPLE,
15981     "normal sounds on/off"
15982   }
15983 #else
15984   {
15985     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15986     GAME_CTRL_ID_STOP,
15987     "stop game"
15988   },
15989   {
15990     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15991     GAME_CTRL_ID_PAUSE,
15992     "pause game"
15993   },
15994   {
15995     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15996     GAME_CTRL_ID_PLAY,
15997     "play game"
15998   },
15999   {
16000     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16001     SOUND_CTRL_ID_MUSIC,
16002     "background music on/off"
16003   },
16004   {
16005     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16006     SOUND_CTRL_ID_LOOPS,
16007     "sound loops on/off"
16008   },
16009   {
16010     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16011     SOUND_CTRL_ID_SIMPLE,
16012     "normal sounds on/off"
16013   }
16014 #endif
16015 };
16016
16017 void CreateGameButtons()
16018 {
16019   int i;
16020
16021   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16022   {
16023     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16024     struct GadgetInfo *gi;
16025     int button_type;
16026     boolean checked;
16027     unsigned long event_mask;
16028     int x, y;
16029     int gd_xoffset, gd_yoffset;
16030     int gd_x1, gd_x2, gd_y1, gd_y2;
16031     int id = i;
16032
16033     x = DX + *gamebutton_info[i].x;
16034     y = DY + *gamebutton_info[i].y;
16035     gd_xoffset = gamebutton_info[i].gd_x;
16036     gd_yoffset = gamebutton_info[i].gd_y;
16037     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16038     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16039
16040     if (id == GAME_CTRL_ID_STOP ||
16041         id == GAME_CTRL_ID_PAUSE ||
16042         id == GAME_CTRL_ID_PLAY)
16043     {
16044       button_type = GD_TYPE_NORMAL_BUTTON;
16045       checked = FALSE;
16046       event_mask = GD_EVENT_RELEASED;
16047       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16048       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16049     }
16050     else
16051     {
16052       button_type = GD_TYPE_CHECK_BUTTON;
16053       checked =
16054         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16055          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16056          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16057       event_mask = GD_EVENT_PRESSED;
16058       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16059       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16060     }
16061
16062     gi = CreateGadget(GDI_CUSTOM_ID, id,
16063                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16064 #if 1
16065                       GDI_X, x,
16066                       GDI_Y, y,
16067 #else
16068                       GDI_X, DX + gd_xoffset,
16069                       GDI_Y, DY + gd_yoffset,
16070 #endif
16071                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16072                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16073                       GDI_TYPE, button_type,
16074                       GDI_STATE, GD_BUTTON_UNPRESSED,
16075                       GDI_CHECKED, checked,
16076                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16077                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16078                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16079                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16080                       GDI_DIRECT_DRAW, FALSE,
16081                       GDI_EVENT_MASK, event_mask,
16082                       GDI_CALLBACK_ACTION, HandleGameButtons,
16083                       GDI_END);
16084
16085     if (gi == NULL)
16086       Error(ERR_EXIT, "cannot create gadget");
16087
16088     game_gadget[id] = gi;
16089   }
16090 }
16091
16092 void FreeGameButtons()
16093 {
16094   int i;
16095
16096   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16097     FreeGadget(game_gadget[i]);
16098 }
16099
16100 static void MapGameButtons()
16101 {
16102   int i;
16103
16104   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16105     MapGadget(game_gadget[i]);
16106 }
16107
16108 void UnmapGameButtons()
16109 {
16110   int i;
16111
16112   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16113     UnmapGadget(game_gadget[i]);
16114 }
16115
16116 void RedrawGameButtons()
16117 {
16118   int i;
16119
16120   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16121     RedrawGadget(game_gadget[i]);
16122 }
16123
16124 static void HandleGameButtons(struct GadgetInfo *gi)
16125 {
16126   int id = gi->custom_id;
16127
16128   if (game_status != GAME_MODE_PLAYING)
16129     return;
16130
16131   switch (id)
16132   {
16133     case GAME_CTRL_ID_STOP:
16134       if (tape.playing)
16135         TapeStop();
16136       else
16137         RequestQuitGame(TRUE);
16138       break;
16139
16140     case GAME_CTRL_ID_PAUSE:
16141       if (options.network)
16142       {
16143 #if defined(NETWORK_AVALIABLE)
16144         if (tape.pausing)
16145           SendToServer_ContinuePlaying();
16146         else
16147           SendToServer_PausePlaying();
16148 #endif
16149       }
16150       else
16151         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16152       break;
16153
16154     case GAME_CTRL_ID_PLAY:
16155       if (tape.pausing)
16156       {
16157 #if defined(NETWORK_AVALIABLE)
16158         if (options.network)
16159           SendToServer_ContinuePlaying();
16160         else
16161 #endif
16162         {
16163           tape.pausing = FALSE;
16164           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16165         }
16166       }
16167       break;
16168
16169     case SOUND_CTRL_ID_MUSIC:
16170       if (setup.sound_music)
16171       { 
16172         setup.sound_music = FALSE;
16173         FadeMusic();
16174       }
16175       else if (audio.music_available)
16176       { 
16177         setup.sound = setup.sound_music = TRUE;
16178
16179         SetAudioMode(setup.sound);
16180
16181         PlayLevelMusic();
16182       }
16183       break;
16184
16185     case SOUND_CTRL_ID_LOOPS:
16186       if (setup.sound_loops)
16187         setup.sound_loops = FALSE;
16188       else if (audio.loops_available)
16189       {
16190         setup.sound = setup.sound_loops = TRUE;
16191         SetAudioMode(setup.sound);
16192       }
16193       break;
16194
16195     case SOUND_CTRL_ID_SIMPLE:
16196       if (setup.sound_simple)
16197         setup.sound_simple = FALSE;
16198       else if (audio.sound_available)
16199       {
16200         setup.sound = setup.sound_simple = TRUE;
16201         SetAudioMode(setup.sound);
16202       }
16203       break;
16204
16205     default:
16206       break;
16207   }
16208 }